// 定义此宏，将SA_RIBBON_EXPORT定义为空
#ifndef SA_RIBBON_BAR_NO_EXPORT
#define SA_RIBBON_BAR_NO_EXPORT
#endif
// 定义此宏，将SA_COLOR_WIDGETS_API定义为空
#ifndef SA_COLOR_WIDGETS_NO_DLL
#define SA_COLOR_WIDGETS_NO_DLL
#endif

/*** Start of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/
// This file provides an extra level of indirection for the @remap in the template
#include "SARibbon.h"

/*** End of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/

// disable warnings about unsafe standard library calls
#ifdef _MSC_VER
#pragma push_macro("_CRT_SECURE_NO_WARNINGS")
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#pragma warning(push)
#pragma warning(disable : 4996)  // deprecated POSIX names
#endif

// clang-format off

/*** Start of inlined file: qrc_SARibbonResource_Datas.cpp ***/
static const unsigned char qt_resource_data[] = {
  // F:/src/SARibbon/src/SARibbonBar/resource/ArrowUp.png
  0x0,0x0,0x0,0xfb,
  0x89,
  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
  0x0,0x0,0xb,0x0,0x0,0x0,0xa,0x8,0x4,0x0,0x0,0x0,0xc8,0xf9,0x6c,0x8,
  0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,
  0x0,0x0,0x0,0x2,0x62,0x4b,0x47,0x44,0x0,0xff,0x87,0x8f,0xcc,0xbf,0x0,0x0,
  0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe4,0x7,0x1,0x7,0x1f,0x2d,0xc6,0x57,0xc,
  0x2f,0x0,0x0,0x0,0x2f,0x49,0x44,0x41,0x54,0x8,0xd7,0x63,0xfc,0xcf,0x80,0xd,
  0x30,0x31,0x90,0x22,0xcc,0xc2,0xc0,0xc0,0xc0,0xc0,0x8,0x61,0xff,0x87,0x30,0xfe,
  0xa3,0xa8,0xfe,0xf,0xc5,0x28,0x86,0xfc,0x47,0xa5,0x99,0x50,0x4,0xe1,0x6c,0x46,
  0x6a,0x38,0x10,0x0,0xbd,0x94,0xa,0xd,0xc6,0x7b,0xd7,0x10,0x0,0x0,0x0,0x25,
  0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,
  0x32,0x30,0x31,0x37,0x2d,0x31,0x32,0x2d,0x30,0x39,0x54,0x31,0x34,0x3a,0x33,0x36,
  0x3a,0x31,0x36,0x2b,0x30,0x38,0x3a,0x30,0x30,0x70,0x8c,0xd2,0xb,0x0,0x0,0x0,
  0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,
  0x0,0x32,0x30,0x32,0x30,0x2d,0x30,0x36,0x2d,0x33,0x30,0x54,0x32,0x33,0x3a,0x33,
  0x31,0x3a,0x34,0x35,0x2b,0x30,0x38,0x3a,0x30,0x30,0x8a,0xc5,0x22,0x37,0x0,0x0,
  0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Max.svg
  0x0,0x0,0x1,0xe2,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x34,0x39,0x32,0x36,0x33,0x33,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x37,0x34,0x31,0x38,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x38,0x33,0x39,0x2e,
  0x32,0x36,0x30,0x31,0x36,0x20,0x38,0x31,0x39,0x2e,0x32,0x48,0x31,0x39,0x34,0x2e,
  0x35,0x36,0x56,0x31,0x39,0x34,0x2e,0x37,0x34,0x34,0x33,0x32,0x68,0x36,0x34,0x34,
  0x2e,0x37,0x30,0x30,0x31,0x36,0x4d,0x32,0x33,0x35,0x2e,0x33,0x31,0x35,0x32,0x20,
  0x32,0x33,0x35,0x2e,0x36,0x31,0x32,0x31,0x36,0x76,0x35,0x34,0x32,0x2e,0x33,0x30,
  0x30,0x31,0x36,0x68,0x35,0x36,0x33,0x2e,0x36,0x31,0x39,0x38,0x34,0x56,0x32,0x33,
  0x35,0x2e,0x36,0x31,0x32,0x31,0x36,0x48,0x32,0x33,0x35,0x2e,0x33,0x31,0x35,0x32,
  0x7a,0x20,0x6d,0x30,0x20,0x30,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,0x23,0x30,
  0x30,0x30,0x30,0x30,0x30,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x37,0x34,
  0x31,0x39,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,0x67,
  0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Min.svg
  0x0,0x0,0x1,0xd7,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x35,0x36,0x30,0x35,0x30,0x35,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x39,0x37,0x31,0x39,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x36,0x35,0x2e,0x32,
  0x33,0x38,0x38,0x34,0x20,0x34,0x35,0x36,0x2e,0x31,0x35,0x32,0x30,0x34,0x31,0x20,
  0x39,0x35,0x38,0x2e,0x37,0x36,0x30,0x31,0x33,0x37,0x20,0x34,0x35,0x36,0x2e,0x31,
  0x35,0x32,0x30,0x34,0x31,0x6c,0x30,0x20,0x31,0x31,0x31,0x2e,0x36,0x39,0x35,0x39,
  0x31,0x38,0x4c,0x36,0x35,0x2e,0x32,0x33,0x38,0x38,0x34,0x20,0x35,0x36,0x37,0x2e,
  0x38,0x34,0x37,0x39,0x35,0x39,0x20,0x36,0x35,0x2e,0x32,0x33,0x38,0x38,0x34,0x20,
  0x34,0x35,0x36,0x2e,0x31,0x35,0x32,0x30,0x34,0x31,0x7a,0x22,0x20,0x70,0x2d,0x69,
  0x64,0x3d,0x22,0x35,0x39,0x37,0x32,0x30,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,
  0x23,0x30,0x30,0x30,0x30,0x30,0x30,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,
  0x3c,0x2f,0x73,0x76,0x67,0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/ArrowMore.png
  0x0,0x0,0x1,0x1,
  0x89,
  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
  0x0,0x0,0xb,0x0,0x0,0x0,0xa,0x8,0x4,0x0,0x0,0x0,0xc8,0xf9,0x6c,0x8,
  0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,
  0x0,0x0,0x0,0x2,0x62,0x4b,0x47,0x44,0x0,0xff,0x87,0x8f,0xcc,0xbf,0x0,0x0,
  0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe4,0x7,0x1,0x7,0x1f,0x2d,0xc6,0x57,0xc,
  0x2f,0x0,0x0,0x0,0x35,0x49,0x44,0x41,0x54,0x8,0xd7,0x63,0xfc,0xcf,0x80,0xd,
  0x30,0x61,0x15,0x65,0x60,0x61,0x60,0x60,0x60,0x60,0x44,0xd1,0xf2,0x9f,0x11,0xaa,
  0xfa,0x3f,0x23,0xaa,0x20,0xdc,0x10,0x98,0x4,0x8c,0x66,0x42,0x56,0x85,0xd0,0xc5,
  0x88,0xdd,0x25,0x38,0xac,0x64,0x24,0xc9,0xdd,0x0,0x39,0x39,0xd,0x13,0xc6,0xeb,
  0xc,0x98,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,
  0x72,0x65,0x61,0x74,0x65,0x0,0x32,0x30,0x31,0x37,0x2d,0x31,0x32,0x2d,0x30,0x39,
  0x54,0x31,0x34,0x3a,0x33,0x36,0x3a,0x31,0x36,0x2b,0x30,0x38,0x3a,0x30,0x30,0x70,
  0x8c,0xd2,0xb,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,
  0x6d,0x6f,0x64,0x69,0x66,0x79,0x0,0x32,0x30,0x32,0x30,0x2d,0x30,0x36,0x2d,0x33,
  0x30,0x54,0x32,0x33,0x3a,0x33,0x31,0x3a,0x34,0x35,0x2b,0x30,0x38,0x3a,0x30,0x30,
  0x8a,0xc5,0x22,0x37,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,

	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Close_Hover.svg
  0x0,0x0,0x2,0x6a,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x30,0x36,0x38,0x32,0x35,0x36,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x30,0x36,0x37,0x31,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x38,0x31,0x30,0x2e,
  0x36,0x36,0x36,0x36,0x36,0x37,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,
  0x33,0x4c,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,0x32,0x31,0x33,
  0x2e,0x33,0x33,0x33,0x33,0x33,0x33,0x20,0x35,0x31,0x32,0x20,0x34,0x35,0x31,0x2e,
  0x38,0x34,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x20,0x32,0x31,
  0x33,0x2e,0x33,0x33,0x33,0x33,0x33,0x33,0x20,0x32,0x31,0x33,0x2e,0x33,0x33,0x33,
  0x33,0x33,0x33,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x20,0x34,
  0x35,0x31,0x2e,0x38,0x34,0x20,0x35,0x31,0x32,0x20,0x32,0x31,0x33,0x2e,0x33,0x33,
  0x33,0x33,0x33,0x33,0x20,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,
  0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x20,0x38,0x31,0x30,0x2e,0x36,
  0x36,0x36,0x36,0x36,0x37,0x20,0x35,0x31,0x32,0x20,0x35,0x37,0x32,0x2e,0x31,0x36,
  0x20,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,0x38,0x31,0x30,0x2e,
  0x36,0x36,0x36,0x36,0x36,0x37,0x20,0x38,0x31,0x30,0x2e,0x36,0x36,0x36,0x36,0x36,
  0x37,0x20,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,0x35,0x37,0x32,
  0x2e,0x31,0x36,0x20,0x35,0x31,0x32,0x20,0x38,0x31,0x30,0x2e,0x36,0x36,0x36,0x36,
  0x36,0x37,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x7a,0x22,0x20,
  0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x30,0x36,0x37,0x32,0x22,0x20,0x66,0x69,0x6c,
  0x6c,0x3d,0x22,0x23,0x65,0x36,0x65,0x36,0x65,0x36,0x22,0x3e,0x3c,0x2f,0x70,0x61,
  0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,0x67,0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Close.svg
  0x0,0x0,0x2,0x5b,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x30,0x36,0x38,0x32,0x35,0x36,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x30,0x36,0x37,0x31,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x38,0x31,0x30,0x2e,
  0x36,0x36,0x36,0x36,0x36,0x37,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,
  0x33,0x4c,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,0x32,0x31,0x33,
  0x2e,0x33,0x33,0x33,0x33,0x33,0x33,0x20,0x35,0x31,0x32,0x20,0x34,0x35,0x31,0x2e,
  0x38,0x34,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x20,0x32,0x31,
  0x33,0x2e,0x33,0x33,0x33,0x33,0x33,0x33,0x20,0x32,0x31,0x33,0x2e,0x33,0x33,0x33,
  0x33,0x33,0x33,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x20,0x34,
  0x35,0x31,0x2e,0x38,0x34,0x20,0x35,0x31,0x32,0x20,0x32,0x31,0x33,0x2e,0x33,0x33,
  0x33,0x33,0x33,0x33,0x20,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,
  0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x20,0x38,0x31,0x30,0x2e,0x36,
  0x36,0x36,0x36,0x36,0x37,0x20,0x35,0x31,0x32,0x20,0x35,0x37,0x32,0x2e,0x31,0x36,
  0x20,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,0x38,0x31,0x30,0x2e,
  0x36,0x36,0x36,0x36,0x36,0x37,0x20,0x38,0x31,0x30,0x2e,0x36,0x36,0x36,0x36,0x36,
  0x37,0x20,0x37,0x35,0x30,0x2e,0x35,0x30,0x36,0x36,0x36,0x37,0x20,0x35,0x37,0x32,
  0x2e,0x31,0x36,0x20,0x35,0x31,0x32,0x20,0x38,0x31,0x30,0x2e,0x36,0x36,0x36,0x36,
  0x36,0x37,0x20,0x32,0x37,0x33,0x2e,0x34,0x39,0x33,0x33,0x33,0x33,0x7a,0x22,0x20,
  0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x30,0x36,0x37,0x32,0x22,0x3e,0x3c,0x2f,0x70,
  0x61,0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,0x67,0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Min_Hover.svg
  0x0,0x0,0x1,0xd7,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x35,0x36,0x30,0x35,0x30,0x35,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x39,0x37,0x31,0x39,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x36,0x35,0x2e,0x32,
  0x33,0x38,0x38,0x34,0x20,0x34,0x35,0x36,0x2e,0x31,0x35,0x32,0x30,0x34,0x31,0x20,
  0x39,0x35,0x38,0x2e,0x37,0x36,0x30,0x31,0x33,0x37,0x20,0x34,0x35,0x36,0x2e,0x31,
  0x35,0x32,0x30,0x34,0x31,0x6c,0x30,0x20,0x31,0x31,0x31,0x2e,0x36,0x39,0x35,0x39,
  0x31,0x38,0x4c,0x36,0x35,0x2e,0x32,0x33,0x38,0x38,0x34,0x20,0x35,0x36,0x37,0x2e,
  0x38,0x34,0x37,0x39,0x35,0x39,0x20,0x36,0x35,0x2e,0x32,0x33,0x38,0x38,0x34,0x20,
  0x34,0x35,0x36,0x2e,0x31,0x35,0x32,0x30,0x34,0x31,0x7a,0x22,0x20,0x70,0x2d,0x69,
  0x64,0x3d,0x22,0x35,0x39,0x37,0x32,0x30,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,
  0x23,0x65,0x36,0x65,0x36,0x65,0x36,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,
  0x3c,0x2f,0x73,0x76,0x67,0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Normal_Hover.svg
  0x0,0x0,0x2,0xc2,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x34,0x32,0x38,0x33,0x38,0x37,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x36,0x36,0x32,0x36,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x32,0x34,0x36,0x2e,
  0x37,0x20,0x31,0x37,0x38,0x76,0x31,0x31,0x37,0x68,0x2d,0x31,0x31,0x37,0x63,0x2d,
  0x31,0x35,0x2e,0x33,0x20,0x30,0x2d,0x32,0x37,0x2e,0x38,0x20,0x31,0x32,0x2e,0x34,
  0x2d,0x32,0x37,0x2e,0x38,0x20,0x32,0x37,0x2e,0x38,0x56,0x38,0x34,0x36,0x63,0x30,
  0x20,0x31,0x35,0x2e,0x33,0x20,0x31,0x32,0x2e,0x34,0x20,0x32,0x37,0x2e,0x38,0x20,
  0x32,0x37,0x2e,0x38,0x20,0x32,0x37,0x2e,0x38,0x68,0x36,0x31,0x39,0x2e,0x38,0x63,
  0x31,0x35,0x2e,0x33,0x20,0x30,0x20,0x32,0x37,0x2e,0x38,0x2d,0x31,0x32,0x2e,0x34,
  0x20,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x56,0x37,0x32,0x39,0x68,0x31,
  0x31,0x37,0x63,0x31,0x35,0x2e,0x33,0x20,0x30,0x20,0x32,0x37,0x2e,0x38,0x2d,0x31,
  0x32,0x2e,0x34,0x20,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x56,0x31,0x37,
  0x38,0x63,0x30,0x2d,0x31,0x35,0x2e,0x33,0x2d,0x31,0x32,0x2e,0x34,0x2d,0x32,0x37,
  0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x48,0x32,0x37,0x34,
  0x2e,0x35,0x63,0x2d,0x31,0x35,0x2e,0x34,0x20,0x30,0x2d,0x32,0x37,0x2e,0x38,0x20,
  0x31,0x32,0x2e,0x35,0x2d,0x32,0x37,0x2e,0x38,0x20,0x32,0x37,0x2e,0x38,0x7a,0x20,
  0x6d,0x30,0x20,0x31,0x36,0x35,0x2e,0x32,0x68,0x34,0x38,0x32,0x2e,0x34,0x76,0x34,
  0x38,0x32,0x2e,0x34,0x48,0x31,0x35,0x30,0x2e,0x32,0x56,0x33,0x34,0x33,0x2e,0x32,
  0x68,0x39,0x36,0x2e,0x35,0x7a,0x20,0x6d,0x34,0x38,0x2e,0x32,0x2d,0x34,0x38,0x2e,
  0x33,0x76,0x2d,0x39,0x36,0x2e,0x35,0x68,0x35,0x37,0x38,0x2e,0x38,0x76,0x34,0x38,
  0x32,0x2e,0x34,0x68,0x2d,0x39,0x36,0x2e,0x35,0x56,0x33,0x32,0x32,0x2e,0x37,0x63,
  0x30,0x2d,0x31,0x35,0x2e,0x33,0x2d,0x31,0x32,0x2e,0x34,0x2d,0x32,0x37,0x2e,0x38,
  0x2d,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x48,0x32,0x39,0x34,0x2e,0x39,
  0x7a,0x20,0x6d,0x30,0x20,0x30,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x36,
  0x36,0x32,0x37,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,0x23,0x65,0x36,0x65,0x36,
  0x65,0x36,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,0x67,
  0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Unshade.png
  0x0,0x0,0x1,0x85,
  0x89,
  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
  0x0,0x0,0xa,0x0,0x0,0x0,0xa,0x8,0x4,0x0,0x0,0x0,0x27,0x3b,0x7,0x36,
  0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,
  0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x7a,0x25,0x0,0x0,0x80,0x83,
  0x0,0x0,0xf9,0xff,0x0,0x0,0x80,0xe9,0x0,0x0,0x75,0x30,0x0,0x0,0xea,0x60,
  0x0,0x0,0x3a,0x98,0x0,0x0,0x17,0x6f,0x92,0x5f,0xc5,0x46,0x0,0x0,0x0,0x2,
  0x62,0x4b,0x47,0x44,0x0,0xff,0x87,0x8f,0xcc,0xbf,0x0,0x0,0x0,0x9,0x70,0x48,
  0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,
  0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe5,0x8,0x5,0x5,0x1f,0x31,0x17,0xbc,
  0x57,0x2d,0x0,0x0,0x0,0x78,0x49,0x44,0x41,0x54,0x8,0xd7,0x8d,0xce,0x31,0xa,
  0xc2,0x40,0x14,0x6,0xe1,0x79,0xef,0x16,0x56,0x6,0xb,0x49,0x13,0xb0,0x9,0x1e,
  0xc4,0x6b,0x26,0xe4,0x1c,0x76,0xda,0x65,0x85,0xec,0xc6,0xca,0x7a,0xf,0xb0,0xfb,
  0x5b,0x8,0x22,0x68,0xe1,0x94,0x5f,0x35,0x26,0xbe,0x73,0x7e,0x63,0xee,0x3f,0x21,
  0xf7,0x40,0xee,0x26,0xc5,0x50,0x4c,0x88,0x62,0x31,0x4c,0xca,0x1d,0x22,0x5d,0x7,
  0x2d,0xa9,0x58,0xb1,0x25,0xd,0x8a,0x41,0x98,0xa8,0x76,0x9f,0x2f,0xfb,0xb6,0xc2,
  0xec,0x87,0xdb,0xb6,0x75,0x99,0x80,0x6a,0x6b,0x3c,0x37,0x70,0x5c,0x9b,0x9d,0xb,
  0xec,0xb5,0x54,0xed,0x31,0xc2,0xe6,0xe4,0x82,0x37,0xfe,0xf1,0xf9,0x4,0x38,0xee,
  0x34,0x82,0x8d,0xf,0x22,0x2c,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,
  0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,0x32,0x30,0x32,0x31,0x2d,0x30,
  0x38,0x2d,0x30,0x32,0x54,0x32,0x32,0x3a,0x30,0x38,0x3a,0x31,0x32,0x2b,0x30,0x38,
  0x3a,0x30,0x30,0x99,0xd1,0x32,0x9e,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,0x64,
  0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,0x0,0x32,0x30,0x32,0x31,0x2d,
  0x30,0x38,0x2d,0x30,0x34,0x54,0x32,0x31,0x3a,0x33,0x31,0x3a,0x34,0x39,0x2b,0x30,
  0x38,0x3a,0x30,0x30,0x29,0xc7,0x98,0x5d,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,
  0xae,0x42,0x60,0x82,
	// F:/src/SARibbon/src/SARibbonBar/resource/define-color.svg
  0x0,0x0,0x8,0x33,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x36,
  0x38,0x33,0x32,0x39,0x32,0x35,0x30,0x37,0x33,0x31,0x32,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x30,0x34,0x35,0x22,0x20,0x78,
  0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,
  0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,
  0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,0x22,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,
  0x22,0x36,0x34,0x22,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3d,0x22,0x36,0x34,0x22,
  0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x32,0x30,0x34,0x2e,0x34,
  0x20,0x35,0x32,0x34,0x2e,0x39,0x63,0x2d,0x31,0x34,0x2e,0x35,0x20,0x31,0x2e,0x35,
  0x2d,0x32,0x36,0x2e,0x32,0x20,0x31,0x33,0x2e,0x32,0x2d,0x32,0x37,0x2e,0x37,0x20,
  0x32,0x37,0x2e,0x37,0x2d,0x32,0x2e,0x31,0x20,0x31,0x39,0x2e,0x39,0x20,0x31,0x34,
  0x2e,0x36,0x20,0x33,0x36,0x2e,0x37,0x20,0x33,0x34,0x2e,0x36,0x20,0x33,0x34,0x2e,
  0x36,0x20,0x31,0x34,0x2e,0x35,0x2d,0x31,0x2e,0x35,0x20,0x32,0x36,0x2e,0x32,0x2d,
  0x31,0x33,0x2e,0x32,0x20,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x20,0x32,
  0x2d,0x31,0x39,0x2e,0x39,0x2d,0x31,0x34,0x2e,0x38,0x2d,0x33,0x36,0x2e,0x36,0x2d,
  0x33,0x34,0x2e,0x37,0x2d,0x33,0x34,0x2e,0x35,0x7a,0x4d,0x32,0x36,0x35,0x2e,0x34,
  0x20,0x34,0x37,0x33,0x2e,0x37,0x63,0x32,0x31,0x2e,0x38,0x2d,0x31,0x2e,0x39,0x20,
  0x33,0x39,0x2e,0x34,0x2d,0x31,0x39,0x2e,0x35,0x20,0x34,0x31,0x2e,0x34,0x2d,0x34,
  0x31,0x2e,0x34,0x20,0x32,0x2e,0x35,0x2d,0x32,0x38,0x2e,0x35,0x2d,0x32,0x31,0x2e,
  0x32,0x2d,0x35,0x32,0x2e,0x33,0x2d,0x34,0x39,0x2e,0x37,0x2d,0x34,0x39,0x2e,0x37,
  0x2d,0x32,0x31,0x2e,0x38,0x20,0x31,0x2e,0x39,0x2d,0x33,0x39,0x2e,0x34,0x20,0x31,
  0x39,0x2e,0x35,0x2d,0x34,0x31,0x2e,0x34,0x20,0x34,0x31,0x2e,0x34,0x2d,0x32,0x2e,
  0x36,0x20,0x32,0x38,0x2e,0x34,0x20,0x32,0x31,0x2e,0x32,0x20,0x35,0x32,0x2e,0x32,
  0x20,0x34,0x39,0x2e,0x37,0x20,0x34,0x39,0x2e,0x37,0x7a,0x4d,0x34,0x31,0x35,0x2e,
  0x38,0x20,0x32,0x36,0x36,0x2e,0x39,0x63,0x2d,0x32,0x38,0x2e,0x35,0x20,0x31,0x2e,
  0x38,0x2d,0x35,0x31,0x2e,0x36,0x20,0x32,0x34,0x2e,0x39,0x2d,0x35,0x33,0x2e,0x34,
  0x20,0x35,0x33,0x2e,0x34,0x2d,0x32,0x2e,0x32,0x20,0x33,0x34,0x2e,0x35,0x20,0x32,
  0x36,0x2e,0x34,0x20,0x36,0x33,0x2e,0x31,0x20,0x36,0x30,0x2e,0x39,0x20,0x36,0x30,
  0x2e,0x39,0x20,0x32,0x38,0x2e,0x35,0x2d,0x31,0x2e,0x38,0x20,0x35,0x31,0x2e,0x36,
  0x2d,0x32,0x34,0x2e,0x39,0x20,0x35,0x33,0x2e,0x34,0x2d,0x35,0x33,0x2e,0x34,0x20,
  0x32,0x2e,0x31,0x2d,0x33,0x34,0x2e,0x36,0x2d,0x32,0x36,0x2e,0x34,0x2d,0x36,0x33,
  0x2e,0x31,0x2d,0x36,0x30,0x2e,0x39,0x2d,0x36,0x30,0x2e,0x39,0x7a,0x4d,0x36,0x32,
  0x31,0x2e,0x39,0x20,0x32,0x35,0x33,0x2e,0x38,0x63,0x2d,0x33,0x35,0x2e,0x31,0x20,
  0x32,0x2e,0x32,0x2d,0x36,0x33,0x2e,0x34,0x20,0x33,0x30,0x2e,0x36,0x2d,0x36,0x35,
  0x2e,0x36,0x20,0x36,0x35,0x2e,0x36,0x2d,0x32,0x2e,0x37,0x20,0x34,0x32,0x2e,0x34,
  0x20,0x33,0x32,0x2e,0x34,0x20,0x37,0x37,0x2e,0x36,0x20,0x37,0x34,0x2e,0x38,0x20,
  0x37,0x34,0x2e,0x38,0x20,0x33,0x35,0x2e,0x31,0x2d,0x32,0x2e,0x32,0x20,0x36,0x33,
  0x2e,0x34,0x2d,0x33,0x30,0x2e,0x36,0x20,0x36,0x35,0x2e,0x36,0x2d,0x36,0x35,0x2e,
  0x36,0x20,0x32,0x2e,0x38,0x2d,0x34,0x32,0x2e,0x34,0x2d,0x33,0x32,0x2e,0x33,0x2d,
  0x37,0x37,0x2e,0x35,0x2d,0x37,0x34,0x2e,0x38,0x2d,0x37,0x34,0x2e,0x38,0x7a,0x4d,
  0x39,0x36,0x36,0x2e,0x35,0x20,0x32,0x37,0x36,0x2e,0x34,0x63,0x2d,0x30,0x2e,0x35,
  0x2d,0x37,0x2e,0x36,0x2d,0x34,0x2d,0x31,0x34,0x2e,0x36,0x2d,0x39,0x2e,0x38,0x2d,
  0x31,0x39,0x2e,0x36,0x6c,0x2d,0x30,0x2e,0x37,0x2d,0x30,0x2e,0x36,0x63,0x2d,0x35,
  0x2e,0x32,0x2d,0x34,0x2e,0x35,0x2d,0x31,0x31,0x2e,0x39,0x2d,0x37,0x2d,0x31,0x38,
  0x2e,0x38,0x2d,0x37,0x2d,0x38,0x2e,0x33,0x20,0x30,0x2d,0x31,0x36,0x2e,0x32,0x20,
  0x33,0x2e,0x36,0x2d,0x32,0x31,0x2e,0x36,0x20,0x39,0x2e,0x39,0x4c,0x35,0x37,0x34,
  0x20,0x36,0x35,0x32,0x2e,0x34,0x6c,0x2d,0x34,0x33,0x2e,0x35,0x20,0x38,0x35,0x2e,
  0x35,0x20,0x31,0x2e,0x31,0x20,0x30,0x2e,0x39,0x2d,0x34,0x2e,0x39,0x20,0x31,0x31,
  0x2e,0x33,0x20,0x31,0x31,0x2e,0x31,0x2d,0x35,0x2e,0x39,0x20,0x31,0x2e,0x35,0x20,
  0x31,0x2e,0x33,0x20,0x37,0x38,0x2d,0x35,0x34,0x2e,0x33,0x20,0x33,0x34,0x32,0x2e,
  0x33,0x2d,0x33,0x39,0x34,0x63,0x35,0x2d,0x35,0x2e,0x38,0x20,0x37,0x2e,0x34,0x2d,
  0x31,0x33,0x2e,0x32,0x20,0x36,0x2e,0x39,0x2d,0x32,0x30,0x2e,0x38,0x7a,0x22,0x20,
  0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x30,0x34,0x36,0x22,0x3e,0x3c,0x2f,0x70,0x61,
  0x74,0x68,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x38,0x39,0x37,
  0x2e,0x38,0x20,0x34,0x37,0x36,0x2e,0x33,0x63,0x2d,0x31,0x33,0x2e,0x38,0x2d,0x31,
  0x2e,0x34,0x2d,0x32,0x36,0x2e,0x37,0x20,0x37,0x2e,0x34,0x2d,0x33,0x30,0x2e,0x34,
  0x20,0x32,0x30,0x2e,0x37,0x2d,0x36,0x2e,0x39,0x20,0x32,0x34,0x2e,0x36,0x2d,0x31,
  0x39,0x2e,0x33,0x20,0x36,0x34,0x2e,0x35,0x2d,0x33,0x35,0x2e,0x31,0x20,0x39,0x37,
  0x2e,0x38,0x43,0x38,0x30,0x39,0x2e,0x35,0x20,0x36,0x34,0x33,0x20,0x37,0x36,0x37,
  0x2e,0x34,0x20,0x37,0x31,0x30,0x2e,0x31,0x20,0x36,0x39,0x36,0x2e,0x37,0x20,0x37,
  0x35,0x36,0x63,0x2d,0x37,0x32,0x2e,0x32,0x20,0x34,0x36,0x2e,0x39,0x2d,0x31,0x34,
  0x32,0x2e,0x37,0x20,0x35,0x36,0x2e,0x37,0x2d,0x31,0x38,0x39,0x2e,0x32,0x20,0x35,
  0x36,0x2e,0x37,0x2d,0x33,0x37,0x20,0x30,0x2d,0x37,0x32,0x2e,0x32,0x2d,0x36,0x2e,
  0x31,0x2d,0x31,0x30,0x31,0x2e,0x37,0x2d,0x31,0x37,0x2e,0x37,0x2d,0x32,0x36,0x2e,
  0x39,0x2d,0x31,0x30,0x2e,0x35,0x2d,0x34,0x36,0x2e,0x34,0x2d,0x32,0x34,0x2e,0x36,
  0x2d,0x35,0x34,0x2e,0x39,0x2d,0x33,0x39,0x2e,0x37,0x2d,0x33,0x2e,0x34,0x2d,0x36,
  0x2e,0x31,0x2d,0x37,0x2e,0x32,0x2d,0x31,0x32,0x2e,0x39,0x2d,0x31,0x31,0x2e,0x32,
  0x2d,0x32,0x30,0x2e,0x32,0x2d,0x31,0x37,0x2e,0x32,0x2d,0x33,0x31,0x2e,0x31,0x2d,
  0x33,0x36,0x2e,0x36,0x2d,0x36,0x36,0x2e,0x35,0x2d,0x34,0x39,0x2e,0x37,0x2d,0x37,
  0x37,0x2e,0x34,0x2d,0x31,0x35,0x2e,0x39,0x2d,0x31,0x33,0x2e,0x32,0x2d,0x33,0x39,
  0x2e,0x31,0x2d,0x31,0x35,0x2d,0x35,0x39,0x2e,0x38,0x2d,0x31,0x35,0x2d,0x38,0x2e,
  0x31,0x20,0x30,0x2d,0x34,0x30,0x2e,0x38,0x20,0x31,0x2e,0x33,0x2d,0x34,0x38,0x2e,
  0x35,0x20,0x31,0x2e,0x33,0x2d,0x33,0x33,0x2e,0x31,0x20,0x30,0x2d,0x34,0x39,0x2e,
  0x34,0x2d,0x36,0x2e,0x35,0x2d,0x35,0x36,0x2e,0x31,0x2d,0x32,0x32,0x2e,0x34,0x2d,
  0x31,0x37,0x2e,0x38,0x2d,0x34,0x32,0x2e,0x33,0x2d,0x37,0x2e,0x33,0x2d,0x31,0x31,
  0x34,0x2e,0x33,0x20,0x32,0x36,0x2e,0x38,0x2d,0x31,0x38,0x33,0x2e,0x34,0x43,0x32,
  0x30,0x35,0x2e,0x32,0x20,0x33,0x33,0x31,0x2e,0x34,0x20,0x33,0x30,0x30,0x20,0x32,
  0x35,0x33,0x2e,0x33,0x20,0x34,0x31,0x32,0x2e,0x36,0x20,0x32,0x32,0x34,0x63,0x34,
  0x30,0x2d,0x31,0x30,0x2e,0x36,0x20,0x38,0x31,0x2e,0x32,0x2d,0x31,0x38,0x2e,0x39,
  0x20,0x31,0x32,0x31,0x2e,0x33,0x2d,0x31,0x38,0x2e,0x39,0x20,0x38,0x35,0x2e,0x36,
  0x20,0x30,0x20,0x31,0x38,0x37,0x2e,0x38,0x20,0x33,0x32,0x2e,0x38,0x20,0x32,0x35,
  0x32,0x2e,0x35,0x20,0x37,0x37,0x2e,0x32,0x20,0x31,0x31,0x2e,0x34,0x20,0x37,0x2e,
  0x38,0x20,0x32,0x36,0x2e,0x39,0x20,0x35,0x2e,0x38,0x20,0x33,0x35,0x2e,0x37,0x2d,
  0x34,0x2e,0x39,0x20,0x31,0x30,0x2e,0x34,0x2d,0x31,0x32,0x2e,0x36,0x20,0x37,0x2e,
  0x31,0x2d,0x33,0x31,0x2e,0x34,0x2d,0x36,0x2e,0x38,0x2d,0x33,0x39,0x2e,0x38,0x2d,
  0x32,0x33,0x2e,0x33,0x2d,0x31,0x34,0x2d,0x35,0x37,0x2e,0x39,0x2d,0x33,0x34,0x2d,
  0x38,0x36,0x2e,0x33,0x2d,0x34,0x37,0x2e,0x31,0x2d,0x36,0x30,0x2e,0x33,0x2d,0x32,
  0x37,0x2e,0x39,0x2d,0x31,0x32,0x33,0x2e,0x37,0x2d,0x34,0x31,0x2e,0x39,0x2d,0x31,
  0x38,0x39,0x2e,0x32,0x2d,0x34,0x31,0x2e,0x39,0x2d,0x36,0x38,0x2e,0x31,0x20,0x30,
  0x2d,0x31,0x34,0x38,0x2e,0x38,0x20,0x31,0x36,0x2e,0x34,0x2d,0x32,0x31,0x37,0x2e,
  0x32,0x20,0x34,0x37,0x2e,0x32,0x2d,0x37,0x38,0x2e,0x31,0x20,0x33,0x35,0x2d,0x31,
  0x33,0x35,0x2e,0x32,0x20,0x38,0x35,0x2d,0x31,0x37,0x39,0x2e,0x34,0x20,0x31,0x34,
  0x37,0x2e,0x35,0x2d,0x33,0x36,0x2e,0x34,0x20,0x35,0x31,0x2e,0x34,0x2d,0x36,0x37,
  0x2e,0x38,0x20,0x31,0x31,0x31,0x2e,0x31,0x2d,0x38,0x30,0x2e,0x31,0x20,0x31,0x36,
  0x38,0x2e,0x37,0x2d,0x37,0x2e,0x35,0x20,0x33,0x35,0x2e,0x31,0x2d,0x36,0x2e,0x38,
  0x20,0x35,0x37,0x2e,0x34,0x2d,0x32,0x2e,0x34,0x20,0x38,0x37,0x2e,0x38,0x20,0x34,
  0x2e,0x32,0x20,0x32,0x39,0x2e,0x32,0x20,0x31,0x33,0x2e,0x34,0x20,0x35,0x32,0x2e,
  0x35,0x20,0x32,0x36,0x2e,0x39,0x20,0x36,0x37,0x2e,0x35,0x20,0x32,0x32,0x2e,0x34,
  0x20,0x32,0x35,0x2e,0x31,0x20,0x35,0x31,0x2e,0x35,0x20,0x33,0x37,0x2e,0x34,0x20,
  0x38,0x39,0x20,0x33,0x37,0x2e,0x34,0x20,0x31,0x33,0x2e,0x39,0x20,0x30,0x20,0x35,
  0x36,0x2e,0x33,0x2d,0x35,0x20,0x36,0x33,0x2e,0x31,0x2d,0x35,0x20,0x37,0x2e,0x34,
  0x20,0x30,0x20,0x31,0x32,0x2e,0x32,0x20,0x31,0x2e,0x32,0x20,0x31,0x34,0x2e,0x34,
  0x20,0x33,0x2e,0x38,0x20,0x36,0x2e,0x34,0x20,0x37,0x2e,0x34,0x20,0x31,0x34,0x2e,
  0x34,0x20,0x32,0x32,0x2e,0x34,0x20,0x32,0x33,0x2e,0x37,0x20,0x33,0x39,0x2e,0x39,
  0x20,0x37,0x2e,0x35,0x20,0x31,0x34,0x2e,0x31,0x20,0x31,0x35,0x2e,0x39,0x20,0x33,
  0x30,0x2e,0x31,0x20,0x32,0x35,0x2e,0x34,0x20,0x34,0x35,0x2e,0x33,0x20,0x31,0x32,
  0x2e,0x31,0x20,0x31,0x39,0x2e,0x35,0x20,0x33,0x36,0x2e,0x39,0x20,0x34,0x30,0x2e,
  0x34,0x20,0x36,0x36,0x2e,0x35,0x20,0x35,0x35,0x2e,0x39,0x20,0x32,0x37,0x20,0x31,
  0x34,0x2e,0x31,0x20,0x37,0x31,0x2e,0x39,0x20,0x33,0x31,0x20,0x31,0x33,0x32,0x2e,
  0x32,0x20,0x33,0x31,0x20,0x37,0x32,0x20,0x30,0x20,0x31,0x34,0x38,0x2e,0x33,0x2d,
  0x32,0x33,0x2e,0x36,0x20,0x32,0x32,0x36,0x2e,0x37,0x2d,0x37,0x30,0x2e,0x31,0x20,
  0x37,0x34,0x2e,0x39,0x2d,0x34,0x34,0x2e,0x34,0x20,0x31,0x32,0x33,0x2d,0x31,0x31,
  0x38,0x2e,0x39,0x20,0x31,0x35,0x30,0x2e,0x32,0x2d,0x31,0x37,0x33,0x2e,0x36,0x20,
  0x31,0x39,0x2d,0x33,0x38,0x2e,0x33,0x20,0x33,0x34,0x2e,0x37,0x2d,0x38,0x37,0x2e,
  0x32,0x20,0x34,0x33,0x2e,0x38,0x2d,0x31,0x31,0x39,0x2e,0x31,0x20,0x34,0x2e,0x38,
  0x2d,0x31,0x37,0x2e,0x33,0x2d,0x37,0x2d,0x33,0x34,0x2e,0x37,0x2d,0x32,0x34,0x2e,
  0x38,0x2d,0x33,0x36,0x2e,0x35,0x7a,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,
  0x30,0x34,0x37,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,
  0x67,0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Max_Hover.svg
  0x0,0x0,0x1,0xe2,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x34,0x39,0x32,0x36,0x33,0x33,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x37,0x34,0x31,0x38,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x38,0x33,0x39,0x2e,
  0x32,0x36,0x30,0x31,0x36,0x20,0x38,0x31,0x39,0x2e,0x32,0x48,0x31,0x39,0x34,0x2e,
  0x35,0x36,0x56,0x31,0x39,0x34,0x2e,0x37,0x34,0x34,0x33,0x32,0x68,0x36,0x34,0x34,
  0x2e,0x37,0x30,0x30,0x31,0x36,0x4d,0x32,0x33,0x35,0x2e,0x33,0x31,0x35,0x32,0x20,
  0x32,0x33,0x35,0x2e,0x36,0x31,0x32,0x31,0x36,0x76,0x35,0x34,0x32,0x2e,0x33,0x30,
  0x30,0x31,0x36,0x68,0x35,0x36,0x33,0x2e,0x36,0x31,0x39,0x38,0x34,0x56,0x32,0x33,
  0x35,0x2e,0x36,0x31,0x32,0x31,0x36,0x48,0x32,0x33,0x35,0x2e,0x33,0x31,0x35,0x32,
  0x7a,0x20,0x6d,0x30,0x20,0x30,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,0x23,0x65,
  0x36,0x65,0x36,0x65,0x36,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x37,0x34,
  0x31,0x39,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,0x67,
  0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Shade.png
  0x0,0x0,0x1,0x87,
  0x89,
  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
  0x0,0x0,0xa,0x0,0x0,0x0,0xa,0x8,0x4,0x0,0x0,0x0,0x27,0x3b,0x7,0x36,
  0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,
  0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x7a,0x25,0x0,0x0,0x80,0x83,
  0x0,0x0,0xf9,0xff,0x0,0x0,0x80,0xe9,0x0,0x0,0x75,0x30,0x0,0x0,0xea,0x60,
  0x0,0x0,0x3a,0x98,0x0,0x0,0x17,0x6f,0x92,0x5f,0xc5,0x46,0x0,0x0,0x0,0x2,
  0x62,0x4b,0x47,0x44,0x0,0xff,0x87,0x8f,0xcc,0xbf,0x0,0x0,0x0,0x9,0x70,0x48,
  0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,
  0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe5,0x8,0x5,0x5,0x1f,0x31,0x17,0xbc,
  0x57,0x2d,0x0,0x0,0x0,0x7a,0x49,0x44,0x41,0x54,0x8,0xd7,0x8d,0x8e,0x21,0xe,
  0x83,0x40,0x14,0x44,0xdf,0xdf,0x70,0x89,0x2a,0x36,0x55,0x35,0x24,0x35,0x84,0x83,
  0x70,0x4d,0x1a,0xce,0x51,0xb9,0xe,0x4,0xbb,0x54,0xd5,0xc2,0x1,0x76,0xa7,0x2,
  0x51,0x51,0x44,0x67,0xc4,0x4b,0xc6,0xbc,0x31,0xf1,0x1b,0x77,0xb2,0x51,0x1d,0x28,
  0xf6,0x7e,0xc0,0xa5,0x77,0x2,0x30,0x1,0xc5,0xd6,0xf8,0xf4,0xd0,0xad,0xfe,0xea,
  0x4,0x15,0x14,0x7b,0x4d,0xc1,0xdf,0xb,0x4,0x6f,0x53,0x7d,0x73,0x42,0xa4,0x30,
  0x68,0x49,0xd9,0xb2,0x2d,0x69,0x50,0xa,0x82,0xbd,0x19,0x15,0xe7,0x6c,0x42,0x64,
  0x8b,0xf3,0xa8,0xbd,0x41,0x6c,0xad,0xf8,0x76,0x6b,0x75,0x88,0xfe,0xfa,0xf9,0x1,
  0xe6,0xd3,0x3a,0x60,0x85,0x9d,0x6f,0x4f,0x0,0x0,0x0,0x25,0x74,0x45,0x58,0x74,
  0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,0x32,0x30,0x32,0x31,
  0x2d,0x30,0x38,0x2d,0x30,0x32,0x54,0x32,0x32,0x3a,0x30,0x38,0x3a,0x31,0x32,0x2b,
  0x30,0x38,0x3a,0x30,0x30,0x99,0xd1,0x32,0x9e,0x0,0x0,0x0,0x25,0x74,0x45,0x58,
  0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,0x0,0x32,0x30,0x32,
  0x31,0x2d,0x30,0x38,0x2d,0x30,0x34,0x54,0x32,0x31,0x3a,0x33,0x31,0x3a,0x34,0x39,
  0x2b,0x30,0x38,0x3a,0x30,0x30,0x29,0xc7,0x98,0x5d,0x0,0x0,0x0,0x0,0x49,0x45,
  0x4e,0x44,0xae,0x42,0x60,0x82,
	// F:/src/SARibbon/src/SARibbonBar/resource/Titlebar_Normal.svg
  0x0,0x0,0x2,0xc2,
  0x3c,
  0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,
  0x30,0x22,0x20,0x73,0x74,0x61,0x6e,0x64,0x61,0x6c,0x6f,0x6e,0x65,0x3d,0x22,0x6e,
  0x6f,0x22,0x3f,0x3e,0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x73,0x76,
  0x67,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x22,0x2d,0x2f,0x2f,0x57,0x33,0x43,
  0x2f,0x2f,0x44,0x54,0x44,0x20,0x53,0x56,0x47,0x20,0x31,0x2e,0x31,0x2f,0x2f,0x45,
  0x4e,0x22,0x20,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x47,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2f,0x53,
  0x56,0x47,0x2f,0x31,0x2e,0x31,0x2f,0x44,0x54,0x44,0x2f,0x73,0x76,0x67,0x31,0x31,
  0x2e,0x64,0x74,0x64,0x22,0x3e,0x3c,0x73,0x76,0x67,0x20,0x74,0x3d,0x22,0x31,0x37,
  0x30,0x32,0x30,0x31,0x39,0x34,0x32,0x38,0x33,0x38,0x37,0x22,0x20,0x63,0x6c,0x61,
  0x73,0x73,0x3d,0x22,0x69,0x63,0x6f,0x6e,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,
  0x78,0x3d,0x22,0x30,0x20,0x30,0x20,0x31,0x30,0x32,0x34,0x20,0x31,0x30,0x32,0x34,
  0x22,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,0x2e,0x31,0x22,0x20,
  0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
  0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30,0x2f,0x73,0x76,
  0x67,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x36,0x36,0x32,0x36,0x22,0x20,
  0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x31,0x36,0x22,0x20,0x68,0x65,0x69,0x67,0x68,
  0x74,0x3d,0x22,0x31,0x36,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x78,0x6c,0x69,
  0x6e,0x6b,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x77,
  0x33,0x2e,0x6f,0x72,0x67,0x2f,0x31,0x39,0x39,0x39,0x2f,0x78,0x6c,0x69,0x6e,0x6b,
  0x22,0x3e,0x3c,0x70,0x61,0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x32,0x34,0x36,0x2e,
  0x37,0x20,0x31,0x37,0x38,0x76,0x31,0x31,0x37,0x68,0x2d,0x31,0x31,0x37,0x63,0x2d,
  0x31,0x35,0x2e,0x33,0x20,0x30,0x2d,0x32,0x37,0x2e,0x38,0x20,0x31,0x32,0x2e,0x34,
  0x2d,0x32,0x37,0x2e,0x38,0x20,0x32,0x37,0x2e,0x38,0x56,0x38,0x34,0x36,0x63,0x30,
  0x20,0x31,0x35,0x2e,0x33,0x20,0x31,0x32,0x2e,0x34,0x20,0x32,0x37,0x2e,0x38,0x20,
  0x32,0x37,0x2e,0x38,0x20,0x32,0x37,0x2e,0x38,0x68,0x36,0x31,0x39,0x2e,0x38,0x63,
  0x31,0x35,0x2e,0x33,0x20,0x30,0x20,0x32,0x37,0x2e,0x38,0x2d,0x31,0x32,0x2e,0x34,
  0x20,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x56,0x37,0x32,0x39,0x68,0x31,
  0x31,0x37,0x63,0x31,0x35,0x2e,0x33,0x20,0x30,0x20,0x32,0x37,0x2e,0x38,0x2d,0x31,
  0x32,0x2e,0x34,0x20,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x56,0x31,0x37,
  0x38,0x63,0x30,0x2d,0x31,0x35,0x2e,0x33,0x2d,0x31,0x32,0x2e,0x34,0x2d,0x32,0x37,
  0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x48,0x32,0x37,0x34,
  0x2e,0x35,0x63,0x2d,0x31,0x35,0x2e,0x34,0x20,0x30,0x2d,0x32,0x37,0x2e,0x38,0x20,
  0x31,0x32,0x2e,0x35,0x2d,0x32,0x37,0x2e,0x38,0x20,0x32,0x37,0x2e,0x38,0x7a,0x20,
  0x6d,0x30,0x20,0x31,0x36,0x35,0x2e,0x32,0x68,0x34,0x38,0x32,0x2e,0x34,0x76,0x34,
  0x38,0x32,0x2e,0x34,0x48,0x31,0x35,0x30,0x2e,0x32,0x56,0x33,0x34,0x33,0x2e,0x32,
  0x68,0x39,0x36,0x2e,0x35,0x7a,0x20,0x6d,0x34,0x38,0x2e,0x32,0x2d,0x34,0x38,0x2e,
  0x33,0x76,0x2d,0x39,0x36,0x2e,0x35,0x68,0x35,0x37,0x38,0x2e,0x38,0x76,0x34,0x38,
  0x32,0x2e,0x34,0x68,0x2d,0x39,0x36,0x2e,0x35,0x56,0x33,0x32,0x32,0x2e,0x37,0x63,
  0x30,0x2d,0x31,0x35,0x2e,0x33,0x2d,0x31,0x32,0x2e,0x34,0x2d,0x32,0x37,0x2e,0x38,
  0x2d,0x32,0x37,0x2e,0x38,0x2d,0x32,0x37,0x2e,0x38,0x48,0x32,0x39,0x34,0x2e,0x39,
  0x7a,0x20,0x6d,0x30,0x20,0x30,0x22,0x20,0x70,0x2d,0x69,0x64,0x3d,0x22,0x35,0x36,
  0x36,0x32,0x37,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,0x23,0x30,0x30,0x30,0x30,
  0x30,0x30,0x22,0x3e,0x3c,0x2f,0x70,0x61,0x74,0x68,0x3e,0x3c,0x2f,0x73,0x76,0x67,
  0x3e,
	// F:/src/SARibbon/src/SARibbonBar/resource/ArrowDown.png
  0x0,0x0,0x0,0xfc,
  0x89,
  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
  0x0,0x0,0xb,0x0,0x0,0x0,0xa,0x8,0x4,0x0,0x0,0x0,0xc8,0xf9,0x6c,0x8,
  0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,
  0x0,0x0,0x0,0x2,0x62,0x4b,0x47,0x44,0x0,0xff,0x87,0x8f,0xcc,0xbf,0x0,0x0,
  0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xe4,0x7,0x1,0x7,0x1f,0x2d,0xc6,0x57,0xc,
  0x2f,0x0,0x0,0x0,0x30,0x49,0x44,0x41,0x54,0x8,0xd7,0x63,0xfc,0xcf,0x80,0xd,
  0x30,0x31,0x90,0x22,0xcc,0xc2,0xc0,0xc0,0xc0,0xc0,0x88,0x62,0xd2,0x7f,0x46,0xa8,
  0xea,0xff,0x8c,0xa8,0x82,0x70,0x43,0x60,0x12,0x30,0x9a,0x9,0x59,0x15,0x42,0x17,
  0x23,0x35,0x1c,0x8,0x0,0xcb,0xec,0xa,0x13,0xe2,0xe,0x8b,0x76,0x0,0x0,0x0,
  0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,
  0x0,0x32,0x30,0x31,0x37,0x2d,0x31,0x32,0x2d,0x30,0x39,0x54,0x31,0x34,0x3a,0x33,
  0x36,0x3a,0x31,0x36,0x2b,0x30,0x38,0x3a,0x30,0x30,0x70,0x8c,0xd2,0xb,0x0,0x0,
  0x0,0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,
  0x79,0x0,0x32,0x30,0x32,0x30,0x2d,0x30,0x36,0x2d,0x33,0x30,0x54,0x32,0x33,0x3a,
  0x33,0x31,0x3a,0x34,0x35,0x2b,0x30,0x38,0x3a,0x30,0x30,0x8a,0xc5,0x22,0x37,0x0,
  0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
	// F:/src/SARibbon/src/SARibbonBar/resource/ribbonPanelOptionButton.png
  0x0,0x0,0x1,0x4b,
  0x89,
  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
  0x0,0x0,0x9,0x0,0x0,0x0,0x9,0x4,0x3,0x0,0x0,0x0,0x12,0xbf,0x1b,0x23,
  0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xb1,0x8f,0xb,0xfc,0x61,0x5,
  0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x7a,0x26,0x0,0x0,0x80,0x84,
  0x0,0x0,0xfa,0x0,0x0,0x0,0x80,0xe8,0x0,0x0,0x75,0x30,0x0,0x0,0xea,0x60,
  0x0,0x0,0x3a,0x98,0x0,0x0,0x17,0x70,0x9c,0xba,0x51,0x3c,0x0,0x0,0x0,0x12,
  0x50,0x4c,0x54,0x45,0xff,0xff,0xff,0x22,0x22,0x23,0x2d,0x2d,0x2e,0x27,0x27,0x28,
  0x22,0x22,0x23,0xff,0xff,0xff,0x56,0x4e,0xf2,0x3b,0x0,0x0,0x0,0x4,0x74,0x52,
  0x4e,0x53,0x0,0x0,0xe6,0xf3,0x9f,0x3,0x91,0x56,0x0,0x0,0x0,0x1,0x62,0x4b,
  0x47,0x44,0x5,0xf8,0x6f,0xe9,0xc7,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,
  0xe6,0x4,0xf,0x6,0x1,0x19,0xd8,0xd7,0x1,0x9a,0x0,0x0,0x0,0x26,0x49,0x44,
  0x41,0x54,0x8,0xd7,0x63,0x60,0x0,0x3,0x26,0x25,0x25,0x5,0x20,0x29,0x28,0x28,
  0x80,0x20,0xd,0x85,0x40,0xa4,0xb0,0x12,0x88,0x14,0x72,0x2,0xa9,0x52,0x52,0x82,
  0xa8,0x66,0x0,0x0,0x37,0xe2,0x2,0x18,0x83,0x28,0x69,0xeb,0x0,0x0,0x0,0x25,
  0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x63,0x72,0x65,0x61,0x74,0x65,0x0,
  0x32,0x30,0x31,0x37,0x2d,0x31,0x32,0x2d,0x30,0x39,0x54,0x31,0x34,0x3a,0x33,0x36,
  0x3a,0x31,0x36,0x2b,0x30,0x38,0x3a,0x30,0x30,0x70,0x8c,0xd2,0xb,0x0,0x0,0x0,
  0x25,0x74,0x45,0x58,0x74,0x64,0x61,0x74,0x65,0x3a,0x6d,0x6f,0x64,0x69,0x66,0x79,
  0x0,0x32,0x30,0x32,0x32,0x2d,0x30,0x34,0x2d,0x31,0x34,0x54,0x32,0x32,0x3a,0x30,
  0x31,0x3a,0x32,0x35,0x2b,0x30,0x38,0x3a,0x30,0x30,0x52,0x37,0x87,0x5,0x0,0x0,
  0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
	// F:/src/SARibbon/src/SARibbonBar/resource/theme-office2013.qss
  0x0,0x0,0x6,0xf8,
  0x0,
  0x0,0x1d,0xff,0x78,0x9c,0xcd,0x58,0xed,0x6f,0xd3,0x46,0x18,0xff,0x1e,0x29,0xff,
  0x83,0x47,0xbf,0x6c,0x91,0x42,0xd2,0x84,0x92,0xd6,0xd5,0x26,0xb5,0x81,0xf2,0x5,
  0xf6,0x56,0x34,0x3e,0x4c,0xd3,0x74,0xb6,0xcf,0x89,0x85,0xe3,0xf3,0x6c,0x87,0xb6,
  0x9b,0x90,0x10,0x85,0x41,0xb7,0x6e,0x45,0x68,0x63,0x74,0x42,0x53,0x61,0x1b,0xea,
  0x27,0x90,0x60,0x9a,0x4a,0x4b,0xe9,0x3f,0x93,0x38,0xe9,0x27,0xfe,0x85,0x9d,0xcf,
  0x76,0x7c,0x67,0x9f,0x63,0xb7,0x15,0xd2,0x6a,0x21,0x85,0xbb,0xe7,0x9e,0x97,0xdf,
  0xf3,0xdc,0xf3,0x72,0x6f,0xf7,0xe,0x2a,0xa5,0x62,0x61,0x78,0xb0,0xe9,0x3e,0x7c,
  0xee,0x6e,0x3f,0xee,0xed,0x1d,0x20,0x55,0xd5,0x64,0x58,0xab,0x4e,0xd6,0x7,0xbf,
  0xdf,0x1a,0x6c,0xee,0xf,0xd7,0x5e,0x1c,0xfe,0xf9,0xb3,0xbb,0xf5,0xba,0xb7,0xb3,
  0x77,0xf8,0xe4,0x61,0xb1,0x60,0x3b,0xc0,0x72,0x4,0x4b,0x93,0x24,0x64,0x8,0x36,
  0x74,0x8a,0x85,0x52,0xa5,0x58,0x28,0x16,0x2a,0xa5,0xc5,0xb9,0xcf,0xc9,0xea,0x3c,
  0xb0,0xbc,0x25,0xea,0xbf,0xc2,0x77,0xc5,0x82,0x20,0x48,0x40,0xbe,0xda,0xb2,0x50,
  0xd7,0x50,0xca,0x32,0xd2,0x91,0x25,0xa,0x4b,0x6d,0xcd,0x81,0xb3,0x64,0xf,0x59,
  0xa,0xc4,0x2b,0x36,0xd2,0x35,0x45,0x98,0x68,0x54,0xbd,0x8f,0xec,0xf8,0xa4,0x13,
  0x52,0xcd,0xfb,0xf0,0xca,0x75,0x56,0x58,0xd3,0xb1,0xf4,0x26,0x32,0x1c,0xa0,0x19,
  0x90,0x11,0xcb,0x6c,0xa4,0x29,0xe0,0x58,0xc0,0xb0,0x4d,0x60,0x41,0xc3,0x9,0x59,
  0xd3,0xcc,0x17,0x1d,0x7c,0x2,0x2a,0x57,0x34,0xa5,0x5,0x1d,0x9a,0x39,0xb3,0x11,
  0x30,0xf,0x2c,0x98,0x34,0x9d,0xd0,0xa,0x79,0x4a,0xa9,0xc1,0x2a,0x65,0x5f,0xd9,
  0x41,0x66,0x79,0x49,0x53,0x9c,0xb6,0x28,0x54,0xcd,0xe5,0x50,0x24,0x97,0xab,0xa8,
  0x22,0xb9,0x6b,0xfb,0xbc,0x51,0xd7,0xd1,0xb1,0x19,0xa2,0x60,0x20,0x3,0xd2,0x8a,
  0x52,0x67,0x21,0x36,0x3,0x38,0xc8,0xf2,0x4f,0x17,0xb,0xee,0xcb,0x6d,0xf7,0xd6,
  0xc6,0xdb,0xd7,0xeb,0x29,0x14,0xd8,0xbd,0xc3,0xd5,0x75,0x77,0xf3,0xf9,0xe1,0x93,
  0x47,0xd8,0xc9,0x38,0x0,0x7a,0xfb,0x7,0x83,0x5f,0xb6,0x9,0x32,0xc5,0x42,0xff,
  0xe9,0x4d,0xf7,0x8f,0x47,0xee,0xea,0xcb,0xc1,0xde,0x66,0x6f,0xe7,0xa7,0xfe,0xbd,
  0x75,0x7c,0xe0,0x70,0x75,0xbb,0xb7,0xf7,0x6f,0x6f,0xe7,0x47,0xfc,0xbb,0x7f,0xf7,
  0xfb,0xfe,0xda,0x8b,0xc1,0xee,0xc1,0xf0,0xd9,0x9b,0xc1,0xfe,0x33,0x9f,0xc6,0xf,
  0x15,0x2c,0xb4,0xbf,0xf1,0xbc,0xb7,0xf7,0xb7,0xcf,0x11,0xb3,0x1a,0x39,0x5,0x38,
  0xb0,0x85,0xac,0x15,0xe1,0x23,0x21,0x5d,0x2d,0xf7,0xc1,0xab,0xfe,0xeb,0xd,0xc2,
  0xe4,0xe6,0xf0,0xc6,0xaa,0xd3,0x86,0x1d,0x58,0x56,0x80,0x75,0xb5,0x76,0xfa,0x1b,
  0xdb,0xf6,0xe3,0x2d,0xe5,0xb4,0xf,0x57,0xe0,0xdc,0x9,0x9,0xca,0x55,0x99,0x17,
  0x33,0x81,0x16,0x4c,0xb8,0x84,0x9a,0x65,0x84,0xea,0xf5,0xe4,0x91,0x4c,0x4f,0xd1,
  0x7e,0xa,0xf,0xf9,0x20,0xbe,0x2b,0xef,0x71,0x2d,0x4b,0xc5,0x3c,0x5,0x35,0x41,
  0xe8,0x0,0xab,0xa5,0x19,0x5e,0xd4,0x8a,0x75,0x12,0xae,0xa3,0x25,0x9,0x39,0xe,
  0xea,0x4,0xab,0x31,0x78,0x3f,0x5,0x6,0xd4,0x69,0xd,0xc8,0x42,0xfe,0x1c,0x10,
  0x40,0x17,0x69,0x74,0x96,0xfc,0xf1,0x2e,0xe8,0x7c,0x17,0xab,0x61,0x5c,0xc0,0xc,
  0xcd,0xe4,0x25,0x4d,0x6c,0x1e,0x21,0xb,0xc4,0xac,0xb9,0x8,0x24,0x6c,0x1,0xf9,
  0x89,0x1d,0xe7,0x6e,0xdd,0xc1,0xa9,0xd0,0xdd,0xda,0xf0,0x33,0x1f,0x87,0x92,0x8,
  0xca,0x16,0xe5,0xd1,0xf0,0x8d,0x8c,0xc9,0xff,0xc4,0x74,0xb4,0xd0,0x20,0x8e,0x50,
  0x7a,0x3b,0x9f,0x91,0x91,0xdc,0x7a,0xbd,0x1e,0xcf,0x43,0x9,0x9e,0x62,0x1b,0x5d,
  0x4b,0x4f,0xa2,0x13,0x32,0x84,0xd,0x55,0x66,0x7c,0x48,0x65,0x37,0xda,0x98,0x39,
  0xd3,0xd4,0x35,0x19,0xb0,0xc6,0xa4,0x6e,0x32,0x71,0x19,0x8f,0x13,0x92,0x4b,0x75,
  0xa8,0x3a,0x65,0xb,0x28,0x5a,0xd7,0x16,0x85,0x5a,0x10,0xa2,0xd4,0xbe,0xa5,0xb5,
  0xda,0x1c,0x82,0xa4,0x9,0x35,0x69,0xaa,0x31,0x3,0xe2,0x40,0x24,0x34,0xca,0x0,
  0x62,0x6a,0x7a,0x7a,0x5a,0xa9,0x66,0x73,0x31,0x2d,0x68,0xdb,0x50,0x49,0xe5,0x53,
  0xaf,0x9f,0x9d,0x91,0x66,0xb2,0xf9,0x64,0x25,0x9e,0xf4,0x93,0x62,0x7,0x1a,0xdd,
  0xb2,0x66,0x28,0xde,0x32,0xa,0x2c,0xaa,0x94,0xec,0xae,0x24,0xe3,0x9a,0x69,0x21,
  0xbd,0x6c,0x22,0x5b,0xf3,0xe,0x88,0x2,0x1,0x71,0xd6,0xf3,0x94,0x20,0xf8,0xc5,
  0xab,0xca,0xbb,0xf6,0x97,0x81,0x14,0xab,0xfc,0xfe,0xca,0x91,0x6a,0x2f,0x7b,0x54,
  0x14,0x1d,0x20,0xb1,0xd9,0x69,0xaa,0x81,0xfd,0xa4,0xce,0xb2,0x15,0x77,0x39,0xa8,
  0xb8,0xf1,0x18,0x8f,0x84,0x26,0xe3,0x9f,0xca,0x6d,0x41,0xb4,0x8e,0xd6,0x88,0xc1,
  0x89,0x55,0x2f,0xd6,0x44,0x61,0x8a,0x9b,0x7,0x23,0x5a,0xbc,0x16,0x14,0xf8,0x29,
  0x4e,0x85,0xa7,0xcc,0x12,0x6d,0xa8,0x43,0xd9,0x9,0xa3,0x80,0x67,0x1f,0xa5,0x7d,
  0x14,0xfd,0x95,0x52,0xd2,0xee,0xb0,0xd3,0xf0,0x7d,0x14,0xc5,0xbf,0x98,0x24,0xa1,
  0x8,0x7c,0x33,0xc7,0x92,0x10,0x9b,0xc7,0x52,0x4,0x0,0x44,0x34,0x54,0xa1,0xe4,
  0xdb,0x4d,0xae,0x90,0xf8,0x1e,0x6b,0x3e,0x17,0x80,0x34,0x8,0xf2,0x80,0x90,0x3,
  0x86,0x5c,0x40,0xe4,0x81,0x22,0x27,0x18,0xcc,0x65,0x41,0x48,0x8f,0x32,0x60,0xa5,
  0x24,0x50,0x58,0x8d,0xb6,0x62,0x45,0xbe,0x7f,0x70,0xfb,0xf0,0xf1,0x5e,0xff,0xfe,
  0xba,0xe9,0xe5,0x67,0xf7,0xee,0x83,0xe1,0x8d,0xdb,0x72,0x50,0xdc,0xbd,0xe6,0x8c,
  0x10,0xf5,0x76,0x6e,0xc,0xef,0xfc,0x53,0x2c,0xe0,0x7e,0x6c,0xb8,0xba,0x1f,0xf6,
  0x66,0xbb,0x54,0xf4,0x7b,0x8d,0xd5,0xbd,0xa7,0xfd,0xbb,0x9b,0xfd,0x47,0xdb,0x73,
  0xb2,0x77,0xc5,0x2f,0xe1,0x5c,0x80,0xbb,0x7f,0xdc,0x74,0xe1,0xe2,0x86,0xb7,0x7b,
  0x3b,0x3f,0xe0,0x1f,0xfd,0xf5,0x5d,0xdc,0xe3,0x79,0xfd,0xd8,0xfa,0xda,0xe1,0xfd,
  0x67,0xee,0x6f,0x5b,0xee,0xcb,0x5f,0xdd,0x87,0x6f,0x6,0x7f,0xed,0xa,0x6c,0x87,
  0x11,0x69,0xcc,0xb6,0xc2,0x89,0x42,0xee,0xd7,0x9a,0x8c,0xf6,0x8a,0xc7,0x97,0xce,
  0x74,0x1c,0xcf,0x4b,0x33,0xa,0x54,0x41,0x4e,0x51,0x13,0xaa,0xac,0x2a,0xea,0x38,
  0x61,0x4c,0x7a,0xe,0x4f,0x55,0xab,0xd5,0x94,0xac,0x33,0x51,0xc3,0xd9,0x5a,0x3d,
  0x93,0x26,0x6e,0x6,0x2a,0x35,0x35,0x91,0xcc,0x29,0x71,0x72,0x1b,0x7a,0x6d,0x3f,
  0x2b,0x6e,0xa4,0xff,0x58,0x6b,0xf9,0xc5,0x78,0x9a,0x14,0xe3,0x34,0x71,0x54,0x9,
  0xcb,0x61,0x9b,0x4,0x14,0x75,0xac,0xb0,0x46,0x24,0x2c,0x12,0x77,0x1,0xe8,0x3a,
  0xb4,0x56,0x78,0x41,0x41,0x5f,0x21,0x46,0xd5,0xe8,0x72,0x78,0xf1,0xc8,0x76,0x39,
  0xde,0x4a,0xa,0x3e,0x49,0xa5,0x16,0x9a,0xde,0x97,0x66,0x90,0x5c,0x53,0xaa,0x8a,
  0x1a,0xc7,0xc7,0x13,0x20,0x8a,0x38,0x6,0x3b,0xbe,0x18,0x13,0x28,0x8a,0x66,0xb4,
  0x48,0xbe,0xa7,0xff,0xa5,0xc8,0xe4,0xd4,0xb2,0x24,0xe7,0x58,0xc6,0xcf,0xed,0x3b,
  0x8a,0xc3,0x38,0xdf,0xe5,0xea,0xcb,0x78,0x9e,0xe5,0x9,0x93,0x43,0xb7,0x31,0xc5,
  0x6f,0x92,0xee,0xea,0x68,0x8f,0x5,0xfe,0x66,0x9d,0x16,0x2c,0xa6,0xbb,0x3f,0xf4,
  0xc4,0x31,0xdb,0xd5,0x84,0x78,0xd2,0xea,0x73,0x75,0x20,0x3b,0xbe,0x22,0x76,0x1b,
  0x2d,0x95,0x15,0x28,0x23,0x8b,0xf4,0x43,0xe5,0xd0,0x29,0x58,0xb5,0x63,0xa9,0x72,
  0x84,0x20,0xa3,0x95,0xc9,0x1b,0x12,0xa3,0xec,0x31,0x92,0x2b,0xe9,0x98,0x2a,0x7,
  0x6b,0xba,0x55,0x3d,0xd9,0xbd,0xae,0x94,0x18,0x21,0x5f,0x68,0x70,0xc9,0x44,0x96,
  0xc3,0x45,0x3a,0xdc,0x4c,0x35,0x48,0x25,0x7f,0x11,0xeb,0x70,0xa8,0xfa,0xec,0x22,
  0x6e,0x5f,0xcf,0x2b,0x9a,0x37,0xda,0xe,0xd6,0x5e,0xf5,0xd7,0x1f,0x70,0x6,0x1d,
  0x3c,0xc3,0x8e,0xe8,0x52,0x4d,0x6b,0x56,0x9b,0xb5,0x66,0x3c,0x1d,0x7b,0x79,0x61,
  0x61,0x81,0x2c,0xfa,0x98,0x7b,0xbe,0xe7,0xe1,0x3d,0x3f,0x3f,0xbf,0xd0,0x88,0xd1,
  0xb1,0x77,0xed,0xfa,0x18,0xa5,0x7c,0xd4,0xc3,0xf1,0x6f,0x4c,0xa9,0x88,0x5b,0xdf,
  0xf4,0x8a,0xc0,0x3c,0x5a,0x66,0xac,0x4f,0x88,0x9,0xa9,0x62,0x3d,0x53,0x18,0x89,
  0x1c,0xc0,0xb9,0xb3,0x6d,0x24,0x15,0x75,0x24,0x94,0x2d,0x35,0xa0,0x3a,0xde,0x4d,
  0xe6,0xbc,0x9c,0x24,0x38,0x67,0x4,0x2b,0x55,0x60,0xb3,0x5d,0x31,0xe2,0x9,0xb1,
  0x3f,0x80,0xa4,0xc3,0xac,0x7c,0x49,0xb7,0x96,0xef,0x22,0x36,0x46,0xa,0x89,0x8a,
  0x85,0x67,0x52,0x5,0x2d,0x5,0xb9,0x95,0x9a,0xb6,0x10,0xee,0x41,0x35,0x3c,0x6b,
  0x5,0x75,0x67,0x36,0xb6,0x1d,0xd,0x63,0xb8,0x9d,0xd,0x6,0xb2,0x68,0x1c,0x13,
  0x26,0x61,0x87,0xc9,0x44,0xd1,0xc,0x98,0x47,0x99,0xc,0xf0,0x17,0xce,0x9d,0x3f,
  0x3f,0x9f,0x5a,0x6c,0xe9,0xee,0x66,0x9c,0x30,0x2c,0xa7,0xc,0x2c,0xb,0x2d,0xf9,
  0x72,0xb4,0xe,0x68,0xe1,0x61,0xb5,0x6b,0xe9,0xef,0x8b,0x95,0xf0,0x5c,0x85,0xac,
  0x56,0x70,0xf7,0x85,0xba,0x96,0xc,0x2b,0x73,0x1e,0xfd,0x39,0x7c,0xf2,0xb4,0x69,
  0xb4,0x3e,0x48,0xa6,0xfd,0xd8,0x33,0x16,0x9b,0x2f,0xb8,0x6f,0x5c,0x78,0x6e,0x18,
  0x9b,0xde,0x83,0x1,0x2a,0x5f,0x7b,0x1a,0x3e,0xac,0x2d,0xca,0xd8,0x45,0xdc,0x6,
  0x98,0x3f,0x2e,0x1c,0xb3,0x1b,0xe6,0x89,0xfb,0x92,0x40,0x7a,0x79,0xc5,0x84,0x1f,
  0x9e,0xaa,0x9f,0xfa,0x8a,0x96,0x1e,0xbc,0x7d,0x84,0x11,0x92,0x9c,0x45,0xb3,0xf8,
  0x9d,0x89,0xf1,0x23,0x6f,0x2d,0x47,0x63,0x97,0xd2,0xaf,0xe4,0x69,0x59,0x92,0x6f,
  0xf1,0x2b,0x36,0xae,0x6a,0xec,0xdc,0x44,0x79,0x3b,0xb6,0x9b,0xe7,0xd1,0x41,0xa0,
  0x5c,0xc5,0x7b,0x35,0x89,0xf3,0xcc,0x78,0x6e,0xa9,0x94,0x2e,0x69,0xcc,0x7b,0x56,
  0xfc,0xfc,0xc4,0xe2,0x1c,0xa6,0xd0,0x3a,0xda,0xb7,0xf0,0x8a,0x66,0xe0,0x1b,0x91,
  0xa2,0xea,0xe8,0xb2,0xcb,0x58,0x4b,0x68,0xc5,0xa1,0xb2,0xa0,0x9,0x81,0xe3,0x49,
  0xe,0x7e,0xc6,0x9,0x72,0xdd,0xad,0xcb,0x9a,0xa3,0x43,0x9,0x58,0x5f,0x63,0x95,
  0x4e,0xdb,0xd7,0xc2,0xeb,0x75,0x44,0xdd,0xa9,0x72,0xc7,0x71,0x26,0x9c,0xf2,0xbe,
  0xe3,0x31,0xe,0x66,0xb0,0xd4,0x4e,0x19,0xe0,0x4f,0x1a,0x1,0xf,0x96,0xb3,0x80,
  0x7,0xcb,0x29,0xc0,0x67,0x42,0x9f,0x3,0xfc,0x93,0xc0,0xf,0x96,0x73,0xc2,0xcf,
  0xb1,0x80,0xbe,0x5f,0x27,0xf2,0x0,0x8f,0x37,0x33,0x96,0x9e,0xc0,0xc2,0x8f,0x91,
  0xd5,0x1,0x3a,0x31,0x52,0x18,0xc1,0x3a,0xba,0xde,0x4d,0x1d,0xd9,0x30,0xc3,0x79,
  0x84,0xe6,0x7f,0xe7,0x39,0xa2,0x55,0x3e,0xdf,0x25,0xc,0xc8,0x78,0x47,0x86,0xd3,
  0x93,0x93,0xb5,0xfa,0x31,0xd8,0x32,0x2f,0x17,0xfc,0xf6,0x7b,0xb2,0x51,0x6d,0x4,
  0x53,0xdf,0x7f,0x2e,0xe4,0x25,0x17,
	// F:/src/SARibbon/src/SARibbonBar/resource/theme-dark2.qss
  0x0,0x0,0x8,0x9,
  0x0,
  0x0,0x23,0xc9,0x78,0x9c,0xcd,0x19,0x59,0x6f,0x13,0x47,0xf8,0x3d,0x92,0xff,0xc3,
  0x10,0x5e,0xda,0xa8,0xc6,0x57,0x9c,0x84,0x8d,0x40,0xa,0xa1,0xa2,0xf,0xd0,0x2,
  0x89,0xca,0x3,0x42,0x68,0xec,0x9d,0x24,0x2b,0xd6,0x3b,0xab,0xf5,0x9a,0x24,0x45,
  0x48,0x81,0x70,0x5,0x2,0xa1,0x94,0xbb,0x89,0x92,0xa0,0x52,0x52,0xa9,0x4,0x28,
  0x2d,0xcd,0x49,0xfe,0x8c,0xd7,0xc7,0x13,0x7f,0xa1,0xb3,0x3b,0xbb,0xeb,0xd9,0x7b,
  0x1d,0x28,0xad,0xfd,0x62,0xcf,0xf7,0xcd,0x77,0xcf,0x77,0xcc,0x7c,0xd8,0xdc,0x49,
  0x75,0x81,0x44,0x47,0x59,0x85,0x8a,0xa,0x14,0xa1,0x50,0xc0,0x12,0x28,0x23,0x35,
  0xd1,0xc1,0x43,0xe5,0x1c,0x28,0xab,0x93,0x22,0x2,0xea,0x18,0x2a,0x21,0x90,0x4d,
  0x74,0x68,0x73,0x97,0x1b,0x53,0xd3,0x3,0x15,0x15,0xf,0xe,0x1c,0x6,0xd9,0x74,
  0x36,0x53,0x7f,0x7a,0xa5,0xfe,0x60,0xb6,0xb9,0xf0,0xac,0x79,0xf5,0x76,0x63,0xe6,
  0x8f,0x44,0x47,0xa2,0xa3,0xf6,0xf7,0x1b,0xf2,0xab,0x31,0x3d,0x5b,0x7b,0xf2,0xaa,
  0xf9,0x6c,0x9e,0xfc,0xe6,0xf6,0x66,0xf6,0x67,0xbb,0x73,0x69,0x2,0xfb,0xeb,0xaa,
  0x7,0x96,0xeb,0xee,0xce,0xe6,0x73,0x4,0xf6,0xf3,0x9f,0x4,0x5c,0x5d,0x9b,0xd2,
  0xde,0x5c,0x76,0x62,0x74,0xf3,0xf9,0xfd,0xbd,0x39,0x40,0x40,0x8d,0x1b,0xbf,0xd7,
  0x1e,0xbf,0xaa,0xdd,0x79,0x51,0xdd,0x7c,0xa7,0xb3,0xde,0x7a,0xd8,0x78,0xff,0xa3,
  0x36,0xbb,0xa1,0x2d,0x2e,0x52,0x64,0x22,0xe3,0xcb,0x47,0xd5,0xed,0x9f,0xac,0xad,
  0x3c,0xd4,0xbf,0x84,0xf8,0xec,0x4c,0xf3,0xde,0x2a,0x4b,0xf6,0xc3,0xd6,0xd3,0x16,
  0x6b,0x3,0x3a,0x86,0xcf,0x23,0x85,0xa2,0x10,0x9e,0xb9,0x7c,0xb1,0xb7,0x27,0xd1,
  0xd1,0x9c,0x9a,0xa9,0xae,0xbd,0x6c,0xbc,0x5f,0xaf,0x2d,0x5f,0xb3,0x88,0x66,0x61,
  0x1f,0x5f,0xc8,0x27,0x3a,0xba,0x52,0xba,0xbe,0xa9,0xae,0xa1,0x81,0x93,0x86,0xe1,
  0xe,0x41,0x45,0x5f,0x62,0xfe,0x5e,0x48,0x74,0x0,0x50,0x80,0xc5,0x73,0xa3,0xa,
  0xae,0x48,0x7c,0xb2,0x88,0x45,0xac,0x70,0xc0,0xb4,0x47,0xbf,0x1,0xc5,0xa,0x8f,
  0xc8,0x5a,0x19,0x8b,0x2,0xef,0x80,0x58,0xc8,0x54,0x7,0xb2,0x74,0x51,0x67,0xe7,
  0x60,0x58,0x51,0x55,0x2c,0x1d,0x21,0xb4,0xe5,0x53,0x2,0x3f,0x8a,0xd4,0xaf,0x2c,
  0xc8,0xd0,0x64,0x59,0x45,0x25,0xa,0x27,0x72,0x68,0xf7,0x66,0x4f,0xa,0xa3,0x63,
  0x2a,0xb3,0xa1,0x39,0xbd,0x4d,0x4c,0x59,0xdd,0xde,0xa9,0xdf,0x5f,0xa9,0xbd,0xfc,
  0x85,0xda,0xf4,0xc3,0xd6,0xac,0xc,0x25,0x9,0x89,0xcd,0xeb,0xba,0x53,0x89,0x89,
  0xa9,0x69,0xea,0x9b,0x57,0xaa,0xeb,0x8b,0x5e,0x7c,0x87,0xba,0x6e,0x61,0x2,0x94,
  0x57,0x15,0x28,0x95,0x65,0xa8,0x20,0x49,0xb5,0x74,0x22,0x21,0x18,0x48,0xa5,0xba,
  0x76,0xcb,0x74,0xde,0xea,0xfb,0xfa,0xf6,0x6a,0x28,0x47,0x70,0x10,0x9c,0x18,0xc6,
  0x58,0xa4,0x0,0x60,0x8,0xd0,0xb2,0xb0,0x84,0x25,0xd4,0x4f,0x97,0x3c,0xa6,0x35,
  0xf0,0x22,0x44,0xd5,0x71,0x64,0xc8,0xf3,0x82,0x34,0xca,0x81,0x34,0xc8,0xca,0x13,
  0x54,0xfe,0x8,0xe9,0x5b,0x2,0x51,0x3d,0x80,0x11,0x67,0xfb,0x64,0x5,0x95,0xcb,
  0x88,0xdf,0x57,0x1c,0x43,0xc5,0x73,0x88,0x7,0xf5,0x9b,0xef,0x6a,0x53,0x97,0x40,
  0x1b,0xfa,0x71,0x6,0x21,0x5b,0x4b,0x6f,0x94,0xd1,0x18,0xa6,0x42,0xc6,0xa5,0x69,
  0x8a,0x15,0x83,0x28,0x63,0xda,0x8c,0x3c,0x61,0xc5,0x2f,0x3d,0x1b,0xed,0xf1,0xb4,
  0x4c,0xf0,0x49,0x98,0xb6,0xeb,0x8d,0xc6,0xdc,0xbc,0x76,0xfb,0x1,0x63,0xfd,0x54,
  0xd7,0x31,0x24,0x55,0x28,0xce,0x71,0x2c,0x57,0xe4,0x36,0x5c,0x72,0x5a,0xd6,0x37,
  0x1c,0xc3,0x3c,0x3a,0xd0,0x99,0xe9,0x3c,0x73,0xc1,0x11,0x34,0x49,0x45,0x3f,0x82,
  0x1c,0xc8,0xb0,0x91,0x43,0x7d,0x68,0xb2,0x4f,0x2,0x17,0x6b,0x50,0x5b,0x59,0xd6,
  0xb6,0xe6,0xc8,0x39,0xa4,0x62,0x52,0x99,0x9b,0xd3,0x2b,0xda,0x8d,0x6b,0xed,0x84,
  0x8a,0x4b,0x2e,0x8e,0x2b,0x11,0x3e,0xc9,0x42,0xcc,0x30,0x4a,0x13,0xe3,0xef,0xef,
  0x7,0x44,0xda,0xda,0xab,0xfb,0xd5,0xb5,0x4d,0x26,0x5d,0x52,0xc1,0x49,0xea,0xae,
  0x6e,0xac,0x12,0x29,0x69,0x86,0x34,0x24,0xa3,0xea,0x99,0xe1,0xf4,0xdf,0x2a,0x68,
  0x9,0x11,0xac,0xa2,0x2b,0x80,0xea,0xa4,0x14,0xdd,0x79,0xae,0x6d,0xad,0x6b,0xd7,
  0x37,0x4c,0xb9,0xc,0x31,0x49,0x7a,0xd4,0x2e,0xcd,0xd5,0x5f,0x6c,0xf6,0xc9,0x13,
  0xe4,0x77,0x63,0xf5,0x37,0x3d,0x3b,0xdd,0x9a,0xb1,0xaa,0xcb,0x76,0x7d,0xfe,0x96,
  0x76,0xe3,0xf5,0x6e,0x5,0xcf,0x76,0x9e,0x1,0xfe,0x21,0xd3,0xc7,0x44,0x4c,0xed,
  0xd1,0x92,0x19,0xb3,0x56,0x76,0xae,0xee,0x2c,0xd4,0x66,0x2f,0x69,0xaf,0xe7,0x48,
  0x99,0x6a,0xfc,0xbd,0xb0,0x5b,0xf6,0x69,0x77,0xc0,0x3a,0xb2,0x1c,0x5b,0x77,0x6,
  0x55,0x45,0x1c,0xc4,0x92,0xa,0x5,0x9,0x39,0x4a,0x9e,0x3,0x0,0xda,0x29,0x0,
  0x2d,0x1a,0x43,0x88,0xc0,0xa0,0x8a,0x15,0x2a,0x2f,0xa9,0xce,0x6f,0x57,0x6a,0x57,
  0x74,0xdb,0x7,0x60,0xe8,0xc1,0xc3,0x14,0x76,0xbb,0x4a,0x19,0xec,0x68,0x95,0xe,
  0xd8,0x49,0x25,0xb4,0x82,0x20,0xcf,0xf7,0xe4,0x7a,0xa0,0x8f,0xb6,0x50,0x45,0xa3,
  0x58,0x99,0x74,0x28,0x6a,0xae,0x5,0xe9,0xc8,0xd6,0xf1,0x54,0x57,0x73,0xf9,0x1d,
  0x9,0xe9,0xda,0x3c,0x69,0x25,0xa6,0x6a,0xb,0xcb,0xf5,0x8d,0x1d,0x9d,0x96,0x95,
  0xc9,0x92,0x2a,0x96,0xd9,0x6c,0x46,0xbb,0x12,0x67,0xa,0xb5,0xf8,0x71,0x23,0xb8,
  0x58,0x29,0x53,0xae,0xb8,0xa2,0x8a,0xc4,0xce,0x76,0x61,0xf3,0x58,0xd2,0xda,0x44,
  0x82,0x94,0xd8,0xe8,0x73,0xd8,0xd7,0x36,0xcb,0x41,0x10,0xc7,0xe4,0xbe,0x91,0x60,
  0x6d,0x3c,0xe,0x49,0x1f,0x42,0x5b,0x2c,0xc7,0x52,0xa0,0xc9,0x6d,0xbb,0x79,0x4a,
  0xbe,0xc7,0x32,0x6,0xa1,0xcf,0x67,0x16,0x2a,0x77,0x3c,0x9b,0x4,0x86,0xa1,0x41,
  0xe4,0x28,0x2c,0x10,0x4a,0x96,0xf8,0xb5,0xa5,0xeb,0xcd,0x67,0x8f,0x6b,0x4b,0x73,
  0x1e,0x66,0x14,0x2f,0x24,0xa7,0x1b,0x2d,0x75,0x50,0x37,0xe4,0xc7,0xf9,0x3b,0x59,
  0x15,0xac,0x74,0xe2,0x61,0xc7,0x2,0x4d,0xff,0x50,0xf,0xd8,0x3d,0x57,0x74,0x73,
  0x45,0x97,0x5d,0x42,0x4,0x72,0x61,0xab,0x96,0x87,0xf6,0xde,0x5c,0x6f,0x9e,0xef,
  0xed,0xf5,0x6b,0x99,0x7,0x64,0x59,0x14,0x8a,0xd0,0x5f,0x17,0xf,0xf0,0x82,0x9f,
  0x60,0xec,0xb9,0x4d,0x8a,0x68,0x44,0x4d,0x2a,0x90,0x17,0x2a,0x65,0xe,0x74,0x1b,
  0xc9,0xd2,0x1,0x37,0x12,0xb8,0x17,0x21,0x24,0x7a,0x1d,0x7a,0x7b,0x24,0xa2,0x7a,
  0x7,0x1d,0x1,0xb6,0xe3,0xb,0x23,0xc2,0x76,0x79,0x1f,0x41,0xc6,0x48,0x46,0xc1,
  0xb9,0x28,0x78,0x23,0x2d,0xcf,0x82,0xc4,0xeb,0xcb,0xd8,0x74,0x63,0xaa,0xab,0x5c,
  0x29,0x14,0x49,0xf5,0x50,0xb0,0x98,0x94,0x71,0x59,0xd0,0x37,0x70,0xc0,0xb0,0x60,
  0x3f,0x4d,0x98,0xe3,0x2,0xaf,0x8e,0x71,0x69,0xbf,0x9a,0x34,0xc,0xb,0xae,0xf9,
  0x8b,0xae,0x4,0x9e,0x1,0x9f,0xe4,0xe3,0xdc,0xc9,0x71,0x2a,0x2c,0x38,0xcf,0x26,
  0x1c,0x21,0xdf,0x1e,0x36,0xc5,0xb4,0xf2,0x36,0x4b,0x2f,0xd5,0xa5,0xdd,0x5c,0xaa,
  0xae,0xdd,0xa4,0x73,0xa3,0xde,0x36,0xdc,0xfd,0x55,0xbb,0xf1,0x84,0xe,0x93,0xda,
  0xdd,0x3b,0x4,0xaa,0x5d,0x7d,0x4e,0xa1,0xe4,0x6f,0x75,0xeb,0x29,0x29,0xe,0x84,
  0x14,0xc9,0x29,0xb4,0xc1,0x30,0xeb,0x83,0x2d,0xb4,0xf7,0xb8,0x94,0xa0,0x32,0x2a,
  0x48,0x46,0xed,0x0,0x69,0x33,0xac,0xcc,0x35,0xb3,0x69,0x70,0xad,0xea,0x91,0xca,
  0x81,0xbc,0x73,0xb1,0x80,0x89,0x3f,0x4a,0xc,0x2e,0x59,0xa3,0x46,0x6,0x79,0x6b,
  0xcd,0xea,0x45,0x9c,0x4,0x5c,0x1d,0x4a,0xbe,0xe5,0x13,0x1f,0x2b,0x72,0x65,0x24,
  0xa2,0xa2,0x6a,0xf5,0x60,0x3e,0xe3,0x57,0xec,0x13,0xc1,0x52,0x35,0xce,0x2,0xb7,
  0xc7,0x49,0x3c,0xd5,0xe5,0x1d,0x10,0x4c,0x52,0xb1,0xaa,0xae,0x8d,0x40,0x35,0xb,
  0x45,0x31,0x4c,0x12,0x8a,0x61,0x1a,0xd8,0x3f,0x4c,0x18,0x5b,0x8c,0x8f,0x9,0xaa,
  0xe7,0xe0,0xb0,0xba,0xee,0xf1,0x37,0xa1,0x1d,0x91,0xee,0xe3,0x60,0xb7,0x78,0xce,
  0x12,0xda,0x5a,0x77,0x4e,0xc7,0x9e,0xe1,0x38,0x62,0x36,0x66,0xbc,0x3,0xfc,0xe9,
  0xb3,0xad,0x8a,0x55,0x8f,0x23,0x6,0xc5,0xc0,0x39,0x34,0x96,0x48,0xfe,0x3,0xaf,
  0x77,0xce,0xc,0xa3,0x19,0x22,0xe7,0xee,0x78,0x3a,0x86,0xab,0x5d,0xa8,0xc1,0xda,
  0xf6,0x8,0x14,0x45,0xa4,0x4c,0x32,0xee,0xb,0x71,0x8b,0xaf,0x32,0xcc,0x1c,0x1d,
  0x7d,0xbb,0xa4,0x8f,0x6a,0x6c,0x3e,0xd5,0xff,0xb3,0xc1,0x17,0x71,0x7c,0xed,0x16,
  0xc3,0x61,0x1a,0x9d,0x8,0xc7,0x91,0x58,0x2f,0x51,0x52,0xf6,0xa8,0x91,0x8f,0x57,
  0x1a,0xbd,0x74,0x5c,0xb9,0x25,0xdc,0x85,0x31,0x1d,0xc8,0x50,0x8f,0x76,0xa0,0x87,
  0xa1,0xf7,0xda,0xa8,0x3d,0xb6,0x45,0xd3,0xbb,0x8e,0xc4,0x9d,0xf1,0x2b,0x7a,0x66,
  0x44,0xb0,0x5e,0x32,0x97,0x2,0xbb,0x64,0xa6,0xf3,0xf3,0x4b,0xc3,0x5e,0xeb,0xf5,
  0x19,0x9f,0x40,0xde,0xc6,0x68,0xe9,0x23,0x80,0xb1,0x4e,0xa5,0x28,0x8f,0xe1,0xf1,
  0x24,0x8f,0x8a,0x58,0x31,0x7a,0x80,0xa4,0xe5,0x30,0xc2,0xa6,0xad,0xde,0xd0,0x19,
  0xac,0x7e,0xfc,0xd8,0xc8,0x6a,0x6f,0x8f,0x3b,0x8a,0x42,0xef,0xa0,0x62,0xa6,0x33,
  0x1f,0x2e,0x61,0x5d,0x6b,0x4,0xb,0x9f,0x7e,0x45,0x77,0x87,0x83,0xd5,0xf7,0x2,
  0x1a,0x97,0xb1,0xa2,0x3a,0x73,0xbe,0xb,0x18,0x1d,0x1a,0xa6,0xa7,0xad,0x31,0xe3,
  0xc4,0x51,0xd2,0xda,0x7d,0xcd,0xb,0xfa,0xc8,0x53,0x9f,0x59,0xd7,0x66,0x1f,0xfa,
  0xd,0x37,0x36,0x96,0xe3,0x1c,0x32,0x26,0x62,0x2f,0xbd,0xdb,0x8d,0x4b,0xea,0x1d,
  0x3d,0x78,0xc2,0x6e,0x6f,0x58,0xbc,0x30,0xf7,0x7b,0x44,0x66,0xba,0xea,0x48,0xef,
  0x3a,0x2c,0x33,0xa8,0x97,0x94,0x43,0x78,0x22,0xdc,0x32,0x16,0x56,0x3b,0x65,0xc0,
  0x77,0x36,0xb6,0xf9,0x9e,0x24,0x3,0x5,0xa6,0x85,0x20,0x9c,0x35,0x83,0xf8,0xe9,
  0xb8,0xf,0xe2,0x52,0x1,0x47,0x6b,0x6d,0x62,0x31,0xc7,0x11,0x44,0x66,0x9b,0xf0,
  0x20,0xf1,0xab,0x4,0x1e,0x7e,0xec,0x88,0x14,0xef,0xa0,0x7a,0x49,0x20,0x12,0x14,
  0xb0,0x20,0xa2,0x10,0xd9,0x43,0x43,0xf8,0x5f,0x8a,0x57,0x5b,0x3e,0x8e,0x57,0xc8,
  0x70,0xc9,0xe3,0x71,0x73,0xdc,0x66,0x26,0x27,0x4c,0xfa,0x56,0x81,0xcc,0x4d,0x66,
  0x65,0xed,0x77,0x81,0x5b,0x83,0x15,0x69,0x80,0xcd,0xe1,0xaa,0x35,0x5a,0x81,0xc,
  0x2a,0x39,0x5c,0xd3,0x9a,0xe7,0xe2,0x8,0xe3,0xc8,0x6f,0xee,0x67,0x97,0x88,0x7b,
  0xd7,0x30,0xfa,0x84,0x74,0x12,0x2a,0xa,0x1e,0xa7,0xa4,0x85,0x12,0x1c,0x25,0xb3,
  0x66,0x45,0x11,0xbf,0xe0,0x52,0xd6,0xbe,0x94,0xb1,0x9a,0x22,0x7d,0x23,0xae,0x28,
  0x45,0x94,0x1a,0xd0,0xf1,0xf,0x93,0x9d,0xfb,0x64,0x69,0xf4,0xcb,0x56,0xca,0xf4,
  0xbf,0xdc,0x1b,0x2a,0x12,0xe3,0x88,0xde,0x2b,0x1,0x3f,0x78,0x9c,0x46,0xe3,0x63,
  0x46,0x1c,0x3f,0x9e,0xa7,0xd,0xf5,0x87,0x27,0x65,0x74,0xa0,0x33,0x67,0xdd,0x13,
  0xb3,0x83,0x8a,0x35,0xb6,0x65,0xbc,0x83,0x58,0x14,0xbd,0x6e,0x17,0x3d,0xe3,0x4e,
  0xc3,0x4d,0xae,0x65,0x32,0xfa,0xb6,0x18,0x34,0x60,0xb8,0xa1,0xf1,0x6e,0x81,0xdd,
  0x73,0xc8,0xc5,0x30,0x92,0x31,0x2e,0x41,0x1,0x7d,0x85,0xb6,0x5f,0x17,0xea,0x6f,
  0x37,0xeb,0x9b,0x8b,0xec,0x43,0xa2,0xf5,0xd6,0x23,0x38,0xdc,0xed,0x66,0xb5,0x77,
  0x68,0x80,0x60,0x8,0x25,0xe1,0x7,0x74,0x4a,0x90,0x48,0x1c,0x6,0x28,0x65,0x9f,
  0xaa,0x22,0xd1,0x7,0x29,0x6e,0x7f,0x2b,0x48,0x46,0x50,0xd5,0x85,0x34,0x7f,0xba,
  0x11,0x62,0x45,0xf4,0xb0,0xa0,0x8a,0xa8,0x0,0x95,0xb3,0x44,0xa4,0xb3,0xdf,0x18,
  0xaf,0x88,0xe5,0xf3,0x56,0x68,0xb7,0xa9,0x41,0xf8,0x3d,0x52,0x21,0xab,0x7f,0x77,
  0x47,0x38,0xe2,0x6e,0xa9,0x8,0xc9,0xb7,0x60,0xc5,0xd4,0x31,0x38,0x11,0x65,0x7e,
  0x38,0x11,0x60,0xfe,0x48,0x7,0xc4,0x70,0xc1,0xc7,0x38,0x1,0x4e,0xb4,0xe5,0x4,
  0x1f,0x3d,0x22,0x9f,0xde,0xe2,0xfa,0xc1,0x8f,0x76,0xf0,0xab,0x6a,0x7b,0x7a,0x7e,
  0x8b,0x95,0x12,0x14,0x19,0x55,0x81,0x6d,0x62,0xbb,0x9,0x1d,0x14,0x71,0x19,0x45,
  0x38,0xd2,0xc0,0xf9,0xdf,0x79,0xd1,0x90,0xaa,0x1d,0x3f,0x7a,0xd4,0x88,0xe8,0xe9,
  0x51,0x5f,0x26,0x93,0xcd,0xed,0x82,0x6c,0xf4,0xab,0xe5,0x48,0xa6,0x37,0xdd,0x6b,
  0xb6,0xa,0xff,0x0,0x96,0x89,0x9a,0xef,
	// F:/src/SARibbon/src/SARibbonBar/resource/theme-win7.qss
  0x0,0x0,0x8,0xff,
  0x0,
  0x0,0x24,0x3e,0x78,0x9c,0xcd,0x59,0x5b,0x6f,0xdb,0x46,0x16,0x7e,0x37,0xe0,0xff,
  0x30,0x8d,0x5f,0x1a,0xc3,0xb2,0x44,0xdd,0x4d,0xa3,0xb,0x48,0xb4,0xd4,0x97,0x66,
  0x7b,0x71,0xb0,0x7d,0x28,0x8a,0x5,0x25,0x8e,0x6d,0x22,0x94,0xc8,0x92,0x54,0x2d,
  0x6f,0x10,0x20,0xa9,0xb3,0x4d,0x9c,0x38,0x71,0xd0,0xed,0x76,0x9b,0xd4,0xd9,0x75,
  0xb3,0x9b,0xd6,0xfb,0x50,0xa7,0x37,0x34,0x76,0x6c,0xd7,0x7f,0x46,0xa4,0xec,0xa7,
  0xfe,0x85,0xce,0x70,0x48,0x6a,0x48,0xe,0x45,0xc9,0x9,0xb0,0x2b,0x3,0x86,0x7d,
  0xe6,0xcc,0x99,0xef,0x5c,0xe6,0x9c,0x33,0x47,0xbf,0x1d,0x9e,0xa4,0xa7,0xc1,0xe4,
  0x84,0x61,0x8a,0xba,0x9,0x74,0xb9,0xd1,0x50,0xdb,0xc0,0x80,0xe6,0xe4,0xc4,0xaa,
  0xdc,0x2e,0x1,0xc3,0x5c,0x53,0x20,0x30,0x57,0x60,0xb,0x4e,0x4e,0x4c,0xa7,0x27,
  0x27,0x26,0x27,0xd2,0xd3,0x8b,0x95,0xf7,0x1c,0xbe,0xaa,0xa8,0x13,0x12,0x45,0xb8,
  0x3a,0x39,0x1,0x40,0x43,0x6c,0x5e,0x59,0xd6,0xd5,0x4e,0x5b,0x4a,0x35,0x55,0x45,
  0xd5,0x79,0x30,0x55,0xcb,0xd5,0x8a,0xb5,0xf2,0xbc,0xb3,0xaa,0xea,0x12,0x44,0x34,
  0x43,0x55,0x64,0x9,0x4c,0x95,0x32,0xf8,0x7,0xad,0x5c,0xb,0x49,0xef,0x98,0xa6,
  0xda,0x7e,0x13,0x89,0xd1,0xde,0x97,0xa5,0x65,0x68,0xce,0x78,0x2b,0x8b,0x6b,0x86,
  0x9,0x5b,0x64,0x1d,0x1d,0x69,0x7d,0xb6,0xf9,0x9e,0xbc,0xbc,0x62,0x52,0x1b,0xce,
  0xd6,0x8f,0xed,0x2f,0x9f,0xf5,0x8e,0x4f,0xfa,0x9f,0xef,0xda,0xdf,0xfd,0xc7,0xbe,
  0xff,0x6d,0xef,0xf0,0x97,0xdf,0x8e,0x36,0x35,0xb1,0xdd,0x86,0xca,0xd9,0xad,0xcd,
  0xb3,0xc7,0x4f,0xfa,0x8f,0x6e,0xda,0x9b,0x1b,0x67,0x9f,0xed,0xf5,0xf,0x6f,0xf6,
  0xe,0xfe,0x15,0xe5,0xc7,0xba,0xc5,0x82,0x89,0xd1,0xd3,0xd4,0xc5,0xb6,0xa1,0x89,
  0x3a,0x6c,0x9b,0x3,0x8d,0x40,0xac,0x94,0xde,0xfe,0x5d,0x82,0xe1,0x74,0xef,0xd7,
  0xfe,0xf1,0xde,0xd0,0x13,0xc1,0x1f,0xc0,0xbb,0x97,0x55,0x55,0x21,0xb,0xc0,0x1,
  0x30,0x30,0x66,0x5b,0x6d,0xc3,0x79,0x42,0xf2,0x4c,0x9e,0x77,0x3e,0x2e,0x31,0x9,
  0x2a,0xe6,0xd1,0x44,0x49,0x92,0xdb,0xcb,0x3c,0xc8,0x80,0xac,0xd6,0x25,0xf8,0x13,
  0xd0,0xf,0x0,0x11,0x3d,0xc0,0x8a,0xfa,0x31,0xd4,0x67,0x35,0x1d,0x1a,0x6,0x94,
  0x66,0x9b,0x2b,0xb0,0x79,0x5,0x4a,0xa0,0x7f,0xe7,0x17,0xfb,0xfa,0xd,0x30,0x86,
  0x7e,0xbc,0x23,0xc8,0xd7,0x32,0x1a,0x50,0xf5,0x85,0x5a,0xad,0x9a,0x23,0x20,0x47,
  0x95,0xe9,0xc2,0x1a,0x41,0x28,0x65,0x5a,0x4e,0xeb,0x7a,0xa1,0x5a,0xaf,0x2f,0x94,
  0x32,0x99,0xf1,0xce,0xf4,0x4c,0xf0,0x4a,0xe,0x1d,0xd7,0x1b,0xa7,0x5b,0xdb,0xd6,
  0xbd,0xbf,0x53,0xd6,0x4f,0x4f,0x5f,0x82,0xed,0xe,0xe1,0x79,0x47,0xd5,0x3a,0xda,
  0x18,0x2e,0xf9,0x40,0xc3,0x1b,0x2e,0xa9,0x12,0x7c,0xe3,0x2,0x77,0xe1,0xc3,0xab,
  0x81,0xa0,0x49,0xe9,0xf8,0xa,0xf2,0x80,0xa3,0x23,0x87,0xf8,0xd0,0x3d,0x3e,0x5,
  0x42,0x47,0x3,0x7b,0xf7,0x6b,0xeb,0x68,0xb,0xdd,0x43,0x2,0x93,0x60,0x3e,0x5b,
  0xdf,0xb5,0x6e,0x7f,0x3a,0x4e,0xa8,0x84,0x70,0xf1,0x7c,0xb,0x9d,0x93,0x6a,0x8c,
  0x18,0x46,0xf5,0x5a,0xa1,0x9c,0x99,0x7,0x8,0xad,0xfd,0xec,0xf3,0xde,0xfe,0x21,
  0x41,0xe1,0x6c,0x23,0xc0,0xed,0xaf,0x7e,0xee,0xbd,0xd8,0x43,0x28,0xcf,0x9e,0x6c,
  0x9f,0x6e,0xfc,0xe8,0x20,0x23,0xea,0xb9,0xe1,0xf4,0xbf,0x55,0xd0,0x3,0x31,0x4c,
  0x45,0x27,0x80,0x1c,0x15,0xbf,0xfa,0xd9,0x7e,0xfe,0x3,0xd6,0xe5,0xf0,0x66,0x50,
  0x97,0x3e,0x5a,0xb9,0xff,0xd4,0x3a,0x3a,0xb0,0x6e,0xbd,0x70,0xd1,0x3a,0xe0,0x51,
  0xd2,0xb4,0x6e,0x6c,0xf5,0xbf,0x3d,0x2c,0x6b,0x5d,0xf4,0xf7,0xe9,0xde,0x7f,0x71,
  0xce,0xba,0xbb,0xe1,0xa6,0xad,0xf5,0xe3,0xfe,0xf6,0x5d,0xeb,0xf6,0xf7,0xe7,0x55,
  0x27,0x7b,0xe1,0x43,0xc0,0xe,0xa4,0x32,0x15,0x47,0xf6,0x3f,0x76,0xdc,0x48,0xf6,
  0x72,0x76,0xef,0xe4,0xb1,0xbd,0x79,0xc3,0xfa,0x7e,0xeb,0xf4,0xd7,0x83,0xd3,0xe7,
  0x8f,0xcf,0x7b,0x7c,0x26,0x1c,0xc6,0x81,0xdc,0x47,0x7e,0xd2,0xd3,0xa8,0xf2,0x1,
  0xeb,0x9b,0x4f,0xec,0x7f,0x6e,0xf7,0x8e,0x77,0xec,0xf5,0x9f,0xac,0xbd,0x47,0xbd,
  0x83,0xd,0xef,0xb8,0x77,0x44,0x54,0x52,0x90,0x51,0x10,0xb6,0x58,0x4,0xc8,0x72,
  0x68,0xab,0xb5,0xf5,0xac,0x77,0xf8,0xb4,0xff,0xc3,0x61,0xef,0xe8,0x8,0x6d,0x40,
  0x35,0xe8,0xf4,0xe4,0xa1,0xbd,0xf3,0xdc,0xfe,0xdb,0xbd,0xde,0xf1,0x36,0x3e,0x25,
  0x20,0x13,0xc1,0x8e,0x57,0xe9,0x2a,0x66,0x1f,0xa4,0x7f,0x2a,0x5f,0xc0,0x1c,0x2c,
  0x42,0x5c,0x69,0xa7,0xc1,0xb5,0x41,0xcd,0x4e,0xfe,0x84,0x8a,0xbb,0x60,0xea,0x8a,
  0xa0,0xb6,0x4d,0x51,0x6e,0x43,0x9d,0x36,0x6f,0x60,0x21,0x36,0xea,0x98,0x85,0x70,
  0x20,0x64,0x11,0xa2,0x35,0xd1,0x54,0x75,0xa2,0xce,0xe4,0x84,0xfd,0xd3,0xae,0x7d,
  0x13,0x47,0x5b,0xc,0x7,0xbe,0x44,0xeb,0x9b,0xf6,0xc3,0x67,0xe4,0x16,0xfa,0xd5,
  0xda,0x39,0x6e,0x72,0x82,0xb8,0x7,0xf9,0xa6,0x7f,0xf8,0xb0,0xb7,0x7f,0xcf,0x7a,
  0xb0,0x89,0x83,0x7c,0x7d,0x17,0x95,0x71,0xe2,0x1a,0x74,0xe5,0xac,0x8d,0x1f,0xfb,
  0x2f,0x4e,0x48,0xa5,0x25,0x3c,0x67,0xff,0xbe,0x6f,0xef,0x1c,0xe1,0x10,0x77,0x3c,
  0x43,0x24,0x22,0x51,0xbe,0xaa,0xa2,0x9,0x97,0x55,0x7d,0x8d,0xf2,0x44,0x14,0x96,
  0xfd,0xc5,0x81,0x7b,0x4f,0xb6,0x3e,0x39,0xbd,0xbe,0xee,0xf4,0x4a,0x29,0x49,0xd4,
  0xaf,0x64,0x67,0x3f,0x32,0xc,0xe2,0x81,0x98,0xdd,0x24,0xf4,0xbd,0x7b,0xda,0x80,
  0xcd,0x4c,0x33,0x1b,0x6d,0x84,0x3c,0x14,0x1,0x27,0xb8,0x34,0x7e,0x49,0x6d,0x76,
  0xc,0x47,0x8c,0xda,0x31,0x15,0xe4,0x12,0xbf,0x15,0xb8,0x16,0xe5,0x8e,0x69,0x5a,
  0x56,0x57,0x64,0x13,0xb2,0xbc,0xe4,0xed,0xb,0x46,0xf7,0xab,0xf6,0x1d,0x4b,0xaf,
  0x78,0x8b,0xc7,0xd8,0xc,0x80,0x96,0xa8,0x2f,0xcb,0xed,0x94,0xa9,0x6a,0x7c,0xce,
  0xb9,0xc1,0x3e,0xa9,0xa1,0xa2,0xdb,0xd3,0x72,0xa9,0x21,0xe3,0x3a,0x37,0x8d,0x46,
  0x40,0xae,0xde,0x48,0xed,0x1d,0xa3,0xfb,0x8a,0xf4,0x5e,0xac,0xe3,0xde,0x12,0x1b,
  0xe8,0x8,0x2f,0x6f,0xd8,0x3b,0xb7,0xce,0x9e,0x7c,0x69,0xef,0x6c,0x5,0x7b,0x69,
  0x8a,0x73,0xc4,0x4b,0x46,0xf7,0x7e,0x45,0xe7,0x33,0x48,0x63,0x21,0x4,0x6f,0x6b,
  0xa6,0xec,0x65,0x95,0x88,0xf2,0xf4,0xa2,0x6b,0x88,0xb0,0x9a,0xc9,0x50,0x8,0x39,
  0x64,0x87,0xd8,0x63,0xe8,0x32,0x9d,0xd0,0xeb,0x5,0xb5,0x59,0x34,0x45,0xdc,0x5d,
  0x91,0xd0,0x8,0x1a,0x30,0xb0,0x34,0xfc,0x9e,0xd0,0x12,0x2b,0x9a,0xa6,0xc8,0x4d,
  0x91,0x6d,0x9f,0xc8,0x22,0x15,0x8c,0xde,0x2d,0x62,0x26,0xe5,0x3c,0x57,0xac,0x54,
  0x17,0xa8,0x55,0x1c,0xa7,0x29,0x5,0x2e,0x99,0x29,0x5d,0x94,0xe4,0x8e,0xc1,0x83,
  0xbc,0x1b,0xb4,0xd4,0xba,0x53,0xa,0xa3,0xc,0x11,0x3,0x7d,0x84,0x15,0x42,0xb1,
  0x8e,0x39,0x91,0x3,0x5e,0x37,0x50,0x53,0x20,0x4a,0x3c,0xaa,0x69,0x33,0xa0,0xcb,
  0xf1,0x99,0x19,0xb0,0xe6,0xfc,0xee,0x66,0x9d,0xbf,0xb3,0x3c,0x37,0x63,0xe0,0x7b,
  0x92,0x41,0xc0,0x8a,0xa5,0x7a,0x75,0x61,0x6,0x90,0xff,0x67,0xb,0x60,0x2a,0x5b,
  0x29,0xd4,0x2b,0xc2,0x8c,0x47,0xe0,0xc0,0x14,0x57,0xc9,0x67,0xca,0xe5,0x19,0xfc,
  0x48,0x44,0x34,0xe,0x6b,0x33,0x57,0x11,0xea,0x17,0xc3,0x6e,0x8d,0x58,0x87,0xb8,
  0x35,0xc6,0xab,0x2f,0x1,0xba,0x54,0xad,0x66,0x6b,0x55,0x1a,0x74,0xbe,0x54,0xaa,
  0x9,0xb,0x1,0xd0,0x5c,0xbe,0x26,0xd4,0x29,0xd0,0xe5,0x4c,0x8d,0xab,0x8f,0x2,
  0x9a,0x7e,0x25,0xbc,0x6a,0x5b,0x57,0xab,0x1,0xd8,0xd9,0x7a,0x31,0x57,0xa9,0x5,
  0x60,0xb,0xf9,0x5c,0xb9,0x42,0xc1,0xce,0x15,0xca,0x55,0x61,0x6e,0x4,0xd8,0x9,
  0x11,0x1e,0xbf,0x91,0xb4,0x93,0x72,0x5b,0xc2,0x64,0xd5,0xbd,0x85,0xe9,0x69,0xa3,
  0xd3,0x68,0xa2,0x32,0xaf,0xab,0x4a,0x4a,0x53,0xd,0x19,0x6f,0xe0,0x81,0x13,0x91,
  0xf3,0xf8,0x4a,0x0,0xb0,0x2a,0x4b,0xe6,0xa,0x9f,0x61,0x65,0xd5,0xcb,0x62,0xc3,
  0x1d,0xe,0x4,0x29,0xe3,0xb4,0xb,0xc1,0x9d,0x3c,0x6f,0x8a,0xd,0x2f,0x13,0x86,
  0x93,0xb,0xf3,0xe1,0x44,0x8b,0x4c,0x4f,0x5b,0x77,0x76,0x7a,0xfb,0x77,0x50,0xb7,
  0x68,0x7f,0xfd,0x29,0xae,0xd5,0xf,0xbe,0xb1,0x6e,0x3f,0x3c,0xbb,0xbe,0xd1,0xdb,
  0xff,0xce,0x7a,0x70,0x1f,0xad,0x5a,0x7f,0x7d,0x4a,0x56,0xd1,0xbf,0xbd,0xa3,0x47,
  0xf6,0xf6,0x6,0x12,0x85,0x1b,0x7,0xa7,0xfb,0x25,0x1a,0xd3,0xc8,0x59,0xd9,0x97,
  0x2a,0x41,0x20,0xe3,0xde,0x56,0x9f,0xea,0xf6,0xb5,0x11,0x3a,0x4e,0x2,0x3c,0x28,
  0x84,0xc9,0x6e,0xd1,0xa2,0xf9,0x11,0x95,0xd8,0x1c,0x14,0x6,0x54,0xaf,0x6d,0xe,
  0x8b,0x9,0xb5,0xd3,0x85,0x81,0x9b,0x18,0x86,0xe5,0xd,0xa8,0xc0,0xa6,0x9,0x51,
  0xc,0xb3,0x56,0xe9,0xec,0xfc,0x72,0xd9,0x2b,0x11,0x40,0xd0,0xc3,0x19,0xe7,0x13,
  0xf4,0x30,0x9d,0x56,0xab,0x15,0x61,0x6e,0xa1,0x1a,0x19,0x71,0xc,0x5a,0x9a,0xc1,
  0x3e,0xd7,0xa0,0xd4,0xab,0x8,0x7f,0x86,0x41,0x72,0xb4,0xe6,0x5f,0xb,0x21,0x63,
  0xa0,0xa8,0x9,0x55,0x21,0xb7,0x10,0xaa,0xc0,0x3e,0x74,0xbf,0x66,0x79,0x5,0xff,
  0xdd,0xb7,0xd0,0xe5,0xac,0x49,0xb2,0x9,0x50,0x78,0xf5,0x37,0xe,0xac,0xcd,0x2f,
  0x40,0xb4,0x3,0x41,0x6f,0x16,0x9f,0x2f,0x39,0xec,0x69,0x38,0xb8,0x23,0x6a,0x46,
  0xe7,0x3e,0x44,0x67,0x97,0x4c,0x94,0x42,0x57,0x3a,0xc5,0x28,0xb7,0x73,0x28,0x4b,
  0xd5,0x4b,0x11,0x4e,0x4a,0x33,0x66,0x41,0xa7,0x21,0x53,0x99,0x9f,0x89,0x31,0x58,
  0xd1,0x29,0xdb,0x8,0x78,0x5c,0x52,0x55,0xbb,0xbe,0x69,0x98,0x96,0xf1,0xb8,0x86,
  0x19,0x26,0xac,0x17,0xf3,0x45,0x32,0x38,0x57,0x6d,0x35,0xd4,0xe4,0x73,0x5d,0xae,
  0x40,0x57,0x44,0xa9,0x25,0x64,0x84,0xac,0x90,0x8f,0xa9,0xd2,0x54,0x9b,0x1d,0x2b,
  0x98,0xae,0x98,0x43,0xac,0xe6,0xaa,0xc,0x42,0x9d,0x66,0xbc,0x58,0x88,0x7c,0x22,
  0x36,0x14,0x48,0x75,0xcf,0x20,0x38,0x22,0x64,0xdf,0x9e,0x51,0xe3,0x64,0x48,0x94,
  0xc4,0x83,0xe2,0x25,0x1d,0xa5,0x9,0x49,0x5d,0x75,0xdb,0x4c,0xaa,0xe2,0xa8,0x28,
  0x79,0xc8,0xa8,0xde,0xb8,0x59,0x6c,0x3e,0xb4,0x3c,0x28,0x48,0x28,0xd1,0xb8,0x45,
  0x69,0x50,0x92,0x0,0x7,0x5b,0xac,0xf6,0x7c,0x44,0x30,0xd1,0x74,0x37,0xb4,0xf3,
  0x65,0x4e,0x22,0x59,0xf2,0x91,0xe8,0x94,0xa8,0xeb,0xea,0x2a,0x11,0x2d,0xb7,0xc4,
  0x65,0x54,0xa3,0x3b,0xba,0xf2,0x3a,0x9f,0xf6,0xf6,0xa5,0x1d,0x6a,0x1a,0x75,0x20,
  0x6a,0x47,0x6f,0xc2,0x74,0x5,0xf3,0x2f,0xa0,0x9d,0xb3,0x5a,0x7b,0xf9,0xe2,0x3c,
  0xa3,0x3,0x1e,0xc,0x36,0x2,0xe5,0xd6,0xa7,0x6,0xef,0x60,0x64,0x4c,0x9c,0x70,
  0x69,0x18,0x31,0x4b,0xcd,0x36,0x9d,0x96,0x3,0xc4,0x46,0xab,0x50,0xad,0x67,0xb9,
  0xc0,0xb3,0x28,0x97,0xcb,0xc5,0x5a,0x51,0x58,0xc8,0x15,0xf3,0xb1,0x47,0x5,0x47,
  0xb7,0x23,0x61,0x67,0x7b,0x26,0x3a,0x9a,0x7d,0xa5,0x32,0x3,0xf3,0x46,0x66,0x15,
  0x4b,0x7e,0xde,0x78,0x62,0xdf,0x14,0x15,0x5,0xea,0x6b,0x94,0x1f,0x87,0xa5,0x1c,
  0x7f,0x60,0x45,0x47,0x7,0x9e,0x48,0xd2,0x71,0x81,0xff,0xa7,0x5f,0xd0,0xec,0xc,
  0x40,0x39,0x5,0xff,0xc4,0x3d,0x6a,0xca,0xf9,0xb9,0x6c,0xa5,0x18,0x34,0x5,0x3e,
  0x80,0xe7,0x51,0xd0,0xb4,0xc8,0x31,0xfe,0x5c,0xad,0x10,0xfb,0x7a,0x89,0x24,0xe6,
  0xa8,0x2c,0xbf,0x3d,0x18,0xe9,0x89,0xc8,0xd8,0x1f,0xe3,0x96,0xf8,0x22,0x3a,0x44,
  0x5e,0xd3,0x75,0x46,0xa0,0x7f,0xe3,0xc2,0x43,0xc3,0x90,0x13,0x69,0x27,0xb8,0xa4,
  0x51,0x87,0xc,0x49,0x33,0x5,0x57,0x9c,0x33,0x1d,0x64,0x1c,0xe3,0xd0,0xdd,0x4,
  0xbb,0xa2,0xae,0xa6,0x24,0xd8,0x54,0x75,0xa7,0xff,0x4f,0x79,0x56,0x45,0xf0,0xc7,
  0x7a,0xd6,0x7,0xb,0xc7,0xf0,0x98,0x64,0xa1,0x19,0xd1,0xa9,0x2f,0xf1,0xd4,0x22,
  0xee,0xf3,0x9f,0x5a,0x5c,0xb6,0x9c,0x75,0x88,0xf8,0x6d,0xe5,0xd2,0xca,0x28,0x19,
  0x4d,0x4e,0xa0,0x10,0xaf,0x15,0xca,0x82,0xcb,0xc9,0x61,0x26,0xc4,0x56,0xbd,0x18,
  0x6b,0xf9,0x21,0xfa,0x30,0xea,0xc6,0x90,0x96,0x27,0x20,0xe6,0x4f,0x32,0x5c,0xd5,
  0x54,0xdd,0x1d,0x61,0x30,0x97,0xe2,0xac,0x14,0x98,0xde,0x85,0xe7,0x68,0x8b,0x4d,
  0x54,0x2e,0x15,0xd6,0x2c,0x87,0x6e,0x18,0xb,0x52,0x16,0x66,0x62,0xfd,0x7b,0xfe,
  0x13,0x3f,0x70,0xca,0xdd,0xe5,0x35,0xd,0xbe,0x71,0x21,0xe7,0x4d,0xfa,0xdd,0x76,
  0x9c,0xbc,0xe,0xbc,0x82,0x1d,0x7d,0x1c,0x24,0xc9,0xcb,0x87,0xe4,0x39,0xaf,0x91,
  0xa8,0x38,0x6a,0x54,0xe4,0x7c,0x6b,0x1c,0x2c,0x97,0xd4,0xb4,0x28,0xb4,0x3a,0xe6,
  0x4,0x90,0xf5,0xd2,0xe,0x8b,0xa4,0xcb,0x25,0xe3,0x89,0x9e,0x9e,0xbe,0x24,0x7,
  0x6a,0x78,0x78,0xff,0xd4,0x62,0x5,0x71,0xc8,0x2d,0xf9,0x2f,0xf0,0x7d,0xf4,0x5a,
  0x57,0x57,0x63,0x90,0xfa,0xcd,0x51,0x13,0x81,0x84,0x7a,0xd8,0x89,0x3a,0xd4,0xa0,
  0x68,0xe2,0x93,0xdd,0x3f,0xc3,0xc,0x23,0x35,0x26,0x97,0x65,0x53,0x81,0xd,0x51,
  0xff,0x33,0x82,0x34,0x6b,0x7c,0xec,0xf5,0x26,0x63,0x62,0x1f,0x3a,0x20,0x9a,0x5a,
  0x2a,0x2c,0x15,0x97,0x8a,0xe7,0x13,0x3c,0x7c,0x88,0x33,0xd5,0x14,0xd1,0x4f,0xc3,
  0x37,0xbc,0xd8,0x4d,0x32,0xbc,0xd8,0x8d,0x31,0x7c,0xa2,0xe9,0x47,0x30,0xfe,0xcb,
  0x98,0x5f,0xec,0x8e,0x68,0x7e,0x86,0x6,0x89,0xdf,0x8e,0x8e,0xea,0x1,0x96,0xec,
  0xf8,0x2f,0xbe,0xc7,0xd3,0xf0,0x8f,0xaa,0xde,0x12,0x15,0x47,0x49,0xe0,0x9b,0xd5,
  0x6f,0x83,0x5,0x45,0x35,0x60,0x82,0xf3,0x1c,0x9e,0xff,0x3b,0xcf,0x39,0xa8,0x46,
  0xf3,0x5d,0x44,0x81,0x84,0x79,0x39,0x2c,0x73,0x5c,0x36,0x77,0xe,0xb1,0xc9,0x5f,
  0x26,0x2f,0x71,0xa5,0x4c,0x49,0xf4,0x3c,0xf0,0x3b,0x73,0x6d,0x76,0xbf,
	// F:/src/SARibbon/src/SARibbonBar/resource/theme-dark.qss
  0x0,0x0,0x7,0x52,
  0x0,
  0x0,0x20,0x2b,0x78,0x9c,0xcd,0x59,0x5b,0x6f,0xd4,0x46,0x14,0x7e,0x8f,0x94,0xff,
  0x30,0x24,0x2f,0x6d,0xd4,0x65,0xb3,0x81,0x40,0x70,0x44,0xa5,0x90,0x56,0xf4,0x1,
  0x5a,0x20,0x51,0x79,0x40,0x8,0xcd,0xda,0xb3,0x1b,0xb,0xaf,0xc7,0xb2,0xbd,0x24,
  0x29,0xaa,0x4,0xa,0x5,0x2,0x29,0x41,0x88,0xb6,0x5c,0x82,0x8,0xa8,0x94,0xf4,
  0x81,0x40,0xb,0x82,0x84,0x24,0xe4,0xcf,0xec,0x7a,0xb3,0x4f,0xfc,0x85,0xce,0x78,
  0xec,0xdd,0x19,0x7b,0x6c,0xef,0x6,0x55,0xed,0xfa,0x65,0x3d,0x97,0xef,0xdc,0xcf,
  0x99,0x33,0xfe,0xb8,0xb1,0x9d,0x1f,0x0,0xbd,0x3d,0x8e,0xb,0x6d,0x17,0xd8,0x7a,
  0xb1,0x88,0x4d,0xe0,0x20,0xb7,0xb7,0x47,0x83,0xf6,0x79,0xe0,0xb8,0xb3,0x6,0x2,
  0xee,0x14,0xaa,0xa0,0xde,0x9e,0x81,0x7c,0x6f,0x4f,0x6f,0x4f,0x7e,0x60,0x62,0xec,
  0x94,0xbf,0xee,0x8,0xb4,0xe9,0x10,0xf7,0x7a,0xb1,0xb7,0x7,0x80,0x22,0x54,0xcf,
  0x97,0x6d,0x5c,0x35,0xb5,0x9c,0x8a,0xd,0x6c,0x2b,0xa0,0x7f,0xa8,0x48,0x9f,0x51,
  0x7f,0x16,0xdb,0x1a,0x22,0x63,0xe,0x36,0x74,0x4d,0x98,0x61,0x8b,0xfb,0xfd,0x81,
  0x21,0x32,0xf2,0x23,0xa5,0x26,0xd0,0xab,0xba,0x2e,0x36,0x8f,0x12,0x68,0xeb,0xb4,
  0xae,0x95,0x91,0xfb,0x45,0x38,0x33,0x31,0xeb,0xb8,0xa8,0xc2,0xe6,0x9,0x1b,0xf5,
  0x3b,0xb,0xa7,0xf4,0xf2,0x94,0xcb,0x6d,0x68,0xce,0x6d,0x79,0xf7,0x5e,0xd6,0xb6,
  0xb6,0x1b,0x77,0x57,0xbc,0x17,0xbf,0x7b,0xb7,0x9e,0xd7,0x36,0xde,0x7e,0xdc,0x5c,
  0xb0,0xa0,0x69,0x22,0xa3,0x79,0x6d,0xa1,0xf9,0xe8,0x69,0xe3,0xc1,0x15,0x6f,0x61,
  0xbe,0x79,0x67,0xb5,0xb1,0x71,0xa5,0xb6,0xfe,0x38,0xbe,0x5e,0x90,0x36,0xca,0x4c,
  0x82,0xec,0xae,0xd,0x4d,0xc7,0x82,0x36,0x32,0xdd,0x50,0x26,0xa2,0xf0,0x44,0x94,
  0xda,0xda,0x4d,0xc6,0xc3,0xce,0xea,0x87,0xc6,0xd6,0x6a,0x2a,0x45,0xf0,0x25,0x38,
  0x39,0x89,0xb1,0xc1,0x26,0x80,0xcf,0x40,0x5b,0xc1,0x26,0x36,0xd1,0x28,0x1b,0x6a,
  0x99,0x61,0x3f,0x7d,0x82,0xc1,0x2c,0x56,0xe9,0x1a,0xb,0x6a,0x9a,0x6e,0x96,0x15,
  0x30,0x8,0x86,0xac,0x19,0xc6,0x7f,0x6,0xf7,0x6d,0x86,0x98,0x1c,0x60,0xa,0x5f,
  0x40,0xf6,0x5e,0xcb,0x46,0x8e,0x83,0xb4,0xbd,0xea,0x14,0x52,0xcf,0x23,0xd,0x34,
  0x6e,0xbc,0xf5,0x2e,0x5d,0x6,0x5d,0xc8,0xa7,0xf8,0x40,0x2d,0x29,0xe3,0x4e,0x6,
  0x7,0xe9,0xc3,0x98,0xec,0x14,0x33,0x60,0x2b,0x19,0x74,0x64,0x98,0x3e,0xdd,0x81,
  0x86,0x32,0x26,0xa3,0x1e,0x2a,0xd2,0x67,0x77,0xfa,0xdc,0x59,0x5c,0xaa,0xff,0xfc,
  0xb,0xa7,0xbf,0xfc,0xc0,0x71,0x64,0x56,0xd9,0x9a,0x13,0xd8,0xaa,0x5a,0x5d,0x28,
  0xf5,0x8c,0x45,0x37,0x1c,0xc7,0x1a,0x3a,0xdc,0x57,0xe8,0x3b,0x7b,0x51,0x30,0x7b,
  0xce,0xa6,0x41,0xa4,0x80,0x2,0x6f,0x7b,0x66,0x85,0x80,0x7c,0xe,0x44,0x48,0x3,
  0x6f,0xe5,0x49,0x7d,0x73,0x91,0x44,0x12,0x63,0x93,0xf1,0xdc,0x9c,0x5b,0xa9,0x5f,
  0xbf,0xda,0x8d,0xb1,0x23,0x7c,0x29,0x4a,0x85,0xd0,0xc9,0x15,0x3b,0x73,0x84,0xc0,
  0x66,0x80,0x70,0xeb,0xbd,0xbc,0x5b,0x5b,0xdb,0x60,0x5c,0xf8,0xdb,0x18,0xe3,0xde,
  0xc3,0x37,0xb5,0xf7,0xab,0x84,0xcb,0xe6,0xd3,0xa5,0x9d,0xf9,0xbf,0x7d,0xce,0x98,
  0x78,0x81,0x43,0xfc,0xb7,0x2,0x86,0x4c,0x64,0x8b,0x18,0xb2,0xdd,0x78,0xf8,0xc6,
  0xbb,0xf5,0xac,0xbe,0xb9,0x5e,0xbf,0xf6,0x3e,0x60,0xcc,0xe7,0x93,0x64,0xb8,0xfa,
  0xe5,0xc5,0xc6,0xf3,0x8d,0x11,0x6b,0x86,0xfc,0xdf,0x59,0xfd,0x93,0x26,0x98,0x9b,
  0xf3,0x41,0x8e,0x99,0xdb,0x6a,0x2c,0xdd,0xac,0x5f,0x7f,0xb5,0x5b,0xce,0x87,0xfa,
  0xce,0x2,0xb9,0xcf,0x8c,0x70,0x2e,0xe3,0xfd,0xb6,0x1c,0x38,0x6d,0x98,0x60,0x6b,
  0xdb,0x8f,0xbc,0x85,0xcb,0xf5,0x57,0x8b,0x3b,0x1f,0xd6,0x77,0xde,0x3d,0xda,0x2d,
  0xf9,0xc1,0xa8,0xc7,0xa,0x89,0x8a,0x2f,0x1d,0xe3,0xae,0x6d,0x8c,0x63,0xd3,0x85,
  0xba,0x89,0x84,0xa2,0x25,0x4c,0x80,0x6e,0x72,0x78,0x1b,0x63,0x2,0x91,0x39,0xe8,
  0x62,0x9b,0xf1,0xdb,0xdb,0xe3,0xbd,0x5e,0xf1,0xae,0x50,0xdd,0x27,0xac,0xa0,0xde,
  0x33,0xb7,0xe0,0xdd,0x7f,0xc9,0xdc,0xaf,0x55,0x68,0x7c,0x72,0xac,0xce,0x26,0xec,
  0x64,0x1c,0xca,0xd2,0x88,0x20,0x2d,0x74,0x51,0x19,0xdb,0xb3,0x82,0xa0,0xc1,0x58,
  0x92,0x8c,0x42,0xdd,0x8d,0x6e,0x52,0x4a,0x58,0xad,0x3a,0x6c,0x2b,0xae,0xba,0x6,
  0x51,0x56,0xab,0xc0,0x44,0x68,0x9f,0x80,0xa4,0x9e,0xb2,0x93,0x82,0x30,0xd4,0x99,
  0x6e,0x63,0xe5,0x4b,0x86,0x7e,0xc,0x16,0x9,0x9e,0xff,0x97,0x7a,0xf3,0xf2,0xb5,
  0xe6,0xd3,0x7b,0xde,0xf2,0xa2,0x84,0x28,0x5b,0x99,0x18,0x46,0xf1,0x82,0x17,0xab,
  0x94,0x32,0xfa,0xdf,0x59,0xae,0x1e,0xfa,0x29,0xaf,0xe2,0xd8,0x64,0x20,0x33,0x93,
  0xa8,0x55,0x8f,0xb3,0xf9,0x8,0x4e,0x42,0x22,0x13,0x89,0x54,0xf8,0x7c,0x18,0xc3,
  0x16,0xaa,0x62,0xf4,0x40,0x35,0xe1,0x42,0x5a,0xa8,0x98,0x6b,0x9,0x5e,0xc7,0x4f,
  0x70,0x42,0xe4,0x5c,0x6c,0xe5,0xa6,0x75,0xcd,0x9d,0x22,0x91,0x16,0xc6,0x99,0x74,
  0x13,0xf3,0x18,0xa9,0xc3,0x4,0x69,0x21,0xdc,0x36,0x66,0x59,0x86,0xae,0x42,0xb9,
  0x4a,0x63,0x93,0x17,0x65,0xfa,0x11,0xd8,0x33,0x50,0xc9,0xcd,0xd9,0x50,0xd3,0xab,
  0x8e,0x2,0xf6,0xfb,0x4c,0xa,0xf3,0x7e,0x82,0x8a,0x2f,0x48,0xf,0x88,0x14,0x8e,
  0x98,0xfa,0x93,0xa2,0x4a,0x54,0x7f,0x32,0x8,0x7f,0x10,0xc9,0x38,0x86,0xa4,0xc1,
  0x24,0x6b,0x3d,0x63,0x23,0xab,0x3f,0xba,0xa9,0xd1,0x61,0x1c,0x78,0x53,0x7e,0xc0,
  0xa9,0x16,0x55,0x92,0x1d,0x6d,0x6c,0xe4,0x2c,0xec,0xe8,0x74,0x83,0x2,0x7c,0xd,
  0x8e,0x52,0x33,0x1,0xc0,0x9c,0x61,0x50,0x96,0x73,0x27,0x61,0x31,0xd2,0x21,0xb0,
  0x91,0xe,0xc3,0x51,0x60,0x98,0xed,0x54,0x14,0x17,0x16,0xc5,0x14,0x58,0x1a,0x2c,
  0x15,0x4a,0x43,0x7c,0xe6,0x28,0x58,0x33,0x41,0x73,0xc1,0xe3,0xe5,0x7,0xea,0x37,
  0x96,0x6b,0x6b,0x37,0x48,0xc1,0xf1,0x9e,0x5c,0xa5,0x65,0xf1,0xf6,0x1f,0xf5,0xeb,
  0xf7,0x9b,0x97,0xe6,0x6b,0x6b,0x2f,0xea,0xb7,0x6f,0x91,0xd9,0xfa,0x4f,0xcf,0xd8,
  0x2c,0x79,0xad,0x6d,0x3e,0xf0,0x96,0xe6,0x9,0x14,0x49,0xd5,0xac,0x80,0x32,0x71,
  0xdb,0x4c,0xc7,0xa3,0xb6,0x2,0xed,0xb2,0x6e,0x52,0x1f,0xb,0x83,0xa3,0x35,0x16,
  0x14,0xc5,0xc8,0x28,0xf5,0x54,0x5,0xc,0x8b,0x83,0x45,0x4c,0xec,0x51,0xe1,0xd6,
  0x92,0xb1,0x20,0xe2,0x86,0xc3,0xb1,0xb0,0xd6,0x8a,0x0,0x91,0xa,0x3c,0xdc,0xb6,
  0x89,0x44,0x8b,0x8a,0x83,0xc,0xa4,0xba,0xe1,0x21,0x43,0xd2,0x21,0xa4,0x45,0x44,
  0x3b,0x4d,0xb7,0xb5,0xdd,0x7f,0x70,0x1f,0x7d,0xd2,0x68,0xfa,0x91,0xa2,0xec,0x11,
  0x49,0xc7,0xcc,0xd6,0xf,0xf,0xd0,0x87,0xcb,0x84,0x60,0x7a,0x4a,0x77,0x63,0x4e,
  0xcc,0x23,0xef,0x91,0x8b,0xd3,0x8a,0xbe,0xc,0xef,0x48,0x37,0x6d,0xac,0x11,0x6d,
  0x9f,0x46,0xc4,0x9a,0xd3,0x1e,0x17,0x7b,0xb1,0x58,0x2b,0x96,0xd1,0x89,0x71,0xa9,
  0x7,0xc8,0xf1,0xf9,0x82,0x2c,0x31,0x45,0x2b,0x5d,0x74,0x6a,0x59,0x59,0xef,0x94,
  0xd8,0x26,0x75,0x24,0x83,0xbc,0x75,0x8a,0x77,0x49,0xdd,0x61,0xf2,0x27,0x1e,0x9,
  0xa6,0xd0,0x19,0x74,0x8,0x19,0x2d,0x90,0x21,0xec,0x51,0x68,0x18,0xc8,0x9e,0xe5,
  0xec,0x99,0x62,0x27,0xb9,0x15,0xfc,0x9f,0xdc,0xa,0x31,0x9f,0xa2,0x7d,0x6,0x9f,
  0x2b,0xe9,0x3b,0xef,0xcc,0x9d,0x86,0xa6,0xa0,0x1a,0xa,0xa2,0x28,0x24,0x76,0x2a,
  0xc,0xaa,0x75,0x4c,0x1e,0x4e,0x2c,0x7b,0x31,0xd7,0x8f,0x63,0x45,0x72,0x47,0x87,
  0x66,0xe2,0xf6,0x67,0x9b,0x29,0xae,0xcc,0x92,0x4a,0x1f,0x29,0xa4,0x1a,0xd8,0x47,
  0x48,0xab,0x5,0x59,0x49,0xa,0x6c,0xca,0xeb,0x39,0x18,0x4a,0x14,0x45,0xf5,0x7f,
  0x89,0xa1,0x94,0x62,0x75,0x39,0x6d,0xbf,0xb1,0x91,0x30,0xe0,0x8f,0x33,0x2e,0x9c,
  0x29,0x3c,0x9d,0xd3,0x90,0x8a,0x6d,0xbf,0x42,0xe7,0x42,0x75,0x13,0x32,0x5d,0x1d,
  0x20,0x45,0x77,0x93,0xd1,0xfb,0x14,0x7b,0x4a,0x70,0xf8,0x83,0x68,0x5c,0x31,0xb2,
  0x1c,0x13,0x7,0x49,0xa8,0x47,0x81,0x32,0x85,0x5d,0xdf,0xeb,0x68,0xda,0xc2,0xb6,
  0x2b,0x26,0xe1,0xc8,0x64,0xa2,0x4c,0x25,0xff,0xd7,0x86,0xe,0xfb,0x89,0x93,0xc7,
  0xc8,0xb1,0xe9,0x6b,0x4d,0xa7,0x5d,0x5a,0x63,0x7e,0xbd,0xbe,0xf0,0x6b,0xec,0x8c,
  0x4f,0x3b,0xd2,0x70,0x55,0x52,0x11,0xe3,0x82,0x3f,0x4e,0x3c,0xac,0x67,0x52,0x9f,
  0x62,0xd6,0xa0,0x86,0x97,0x70,0x7d,0x0,0x1e,0x42,0x3e,0xd7,0xfc,0xba,0x78,0x95,
  0x4c,0x64,0x97,0x3b,0xad,0x4a,0x8d,0x44,0x62,0xc,0xa9,0x6a,0xd8,0xba,0xb7,0x74,
  0x32,0x4e,0x93,0xf5,0x11,0x3c,0x93,0xae,0x93,0x70,0x55,0x37,0x9,0x58,0xda,0x5b,
  0xb7,0xe9,0xe2,0x4a,0x11,0x67,0xd3,0xd,0x56,0xc5,0x5d,0xaf,0x2b,0x6b,0xc8,0xf4,
  0x16,0x42,0xf3,0xa7,0xfc,0x14,0xad,0x5,0x42,0x83,0x84,0xd8,0x8b,0xc3,0x22,0x62,
  0x13,0x58,0x34,0x10,0xe7,0xf6,0x20,0xab,0x5a,0xb7,0xbd,0xe7,0x5f,0x71,0x95,0x16,
  0x6f,0x8a,0x66,0x93,0x7e,0x49,0xc3,0xd3,0x41,0x23,0xcb,0x35,0x3,0x98,0x1c,0x32,
  0x75,0xd2,0xa,0x4,0x5,0x65,0x34,0x32,0xdd,0xee,0x15,0xc8,0x69,0x38,0xe8,0x17,
  0xda,0xdd,0x2,0x28,0xa0,0x4a,0x42,0xaf,0xdf,0x9,0x33,0xb2,0x34,0x93,0xd2,0x5c,
  0x4b,0xf3,0x8e,0xc,0x9f,0x40,0xe7,0xa0,0x6d,0xe3,0x69,0x6,0xad,0x57,0x60,0x99,
  0xb4,0x4f,0x55,0xdb,0xf8,0x4c,0xc9,0x87,0xfb,0xf2,0xfe,0x68,0x9e,0x1c,0x87,0x70,
  0xd5,0x56,0x51,0x7e,0x8c,0xae,0xff,0x8a,0xec,0xdc,0x6b,0x99,0xe5,0xcf,0xe5,0x5d,
  0x76,0x78,0x89,0x32,0xa1,0x12,0xd5,0x18,0xf1,0x1e,0x57,0x36,0x9f,0x7c,0xb2,0x4b,
  0x39,0x53,0x74,0xd1,0xc5,0xca,0x68,0x9e,0xf1,0x85,0x9f,0x9c,0xb5,0xd0,0xe1,0xbe,
  0x7d,0xe1,0xc5,0x5e,0xd0,0x38,0xb3,0xa6,0x39,0x34,0x5f,0xbc,0xb3,0xc8,0xc2,0xdb,
  0x1f,0xc1,0xf3,0x9b,0xf4,0x28,0x1c,0x77,0x31,0xe1,0x7f,0xcf,0x49,0x3a,0x66,0x47,
  0x67,0xbb,0xbc,0x5a,0x92,0x35,0xc4,0x51,0xc8,0xe,0x2e,0xbc,0x80,0xf7,0xee,0x2f,
  0x7a,0x79,0x17,0xde,0x7,0x37,0x5e,0x6f,0x34,0x36,0x1e,0xf3,0x1f,0x6f,0xc2,0xdb,
  0x79,0x5d,0x30,0x77,0x94,0x54,0xff,0xc4,0x18,0x59,0xa1,0x57,0xf4,0x1f,0xd0,0x69,
  0xd2,0x7f,0xe3,0xe9,0x4,0xa1,0x5a,0x31,0xa5,0x12,0x79,0x90,0x1d,0xb5,0xb7,0x8d,
  0x2c,0x4,0x5d,0xca,0x64,0xf0,0x37,0xba,0xa0,0x23,0x7f,0x9e,0xd4,0x5d,0x3,0x15,
  0xa1,0x7d,0x8e,0xb0,0x74,0xee,0x1b,0xff,0xcb,0x8d,0x73,0x21,0x74,0xec,0x2e,0x25,
  0x48,0xbf,0x18,0x91,0x5d,0x37,0x76,0xa,0x9c,0x71,0x59,0xa2,0x42,0xf2,0xb4,0xbe,
  0xae,0x1c,0x87,0x33,0x59,0xea,0x87,0x33,0x9,0xea,0xcf,0x34,0x40,0x7,0x26,0xf8,
  0x14,0x23,0xc0,0x99,0xae,0x8c,0x20,0x91,0x23,0xf3,0x63,0x49,0xa7,0x76,0x90,0x61,
  0x27,0x7f,0xe8,0xea,0x4e,0xce,0x6f,0xb1,0x5d,0x81,0x6,0x27,0x2a,0x68,0xa9,0xb8,
  0xd5,0x27,0x8d,0x1b,0xd8,0x41,0x19,0x86,0xf4,0xd7,0xfc,0xef,0xac,0xe8,0x73,0xd5,
  0x8d,0x1d,0x63,0x62,0xa4,0xde,0xf0,0x82,0x7e,0x34,0x52,0x28,0xc,0xed,0xdb,0x5,
  0x6c,0xf6,0x77,0xa6,0x52,0xe1,0xe0,0xe0,0x41,0xc8,0xa0,0xff,0x1,0x27,0xe7,0xc,
  0x76,
	// F:/src/SARibbon/src/SARibbonBar/resource/theme-office2016-blue.qss
  0x0,0x0,0x9,0x55,
  0x0,
  0x0,0x28,0xf8,0x78,0x9c,0xcd,0x5a,0x5b,0x6f,0xdc,0xc6,0x15,0x7e,0x17,0xa0,0xff,
  0xc0,0x5a,0x2f,0xad,0xd0,0xd5,0xae,0x56,0x37,0x6b,0x85,0x1a,0x90,0x14,0x2b,0x5,
  0x14,0xa7,0x71,0x64,0x34,0xf,0x41,0x10,0xf0,0x32,0x2b,0x11,0xe6,0x2e,0x59,0x2e,
  0x37,0x92,0x22,0x8,0xb0,0x23,0xa7,0xb6,0x62,0xc5,0xa,0x8c,0xc4,0xb5,0x1b,0xb9,
  0x71,0xdc,0x5c,0xd4,0x87,0xca,0x49,0x6c,0xc4,0x92,0xd6,0xb6,0xfe,0xcc,0x2e,0x57,
  0xfb,0x94,0xbf,0xd0,0x19,0xe,0x87,0x9c,0x19,0xce,0x90,0x5c,0x15,0x41,0x2b,0xc2,
  0xc0,0x7a,0xe6,0x9c,0x33,0xdf,0xb9,0xcc,0x99,0x73,0x86,0xfc,0xa5,0x75,0x52,0x1c,
  0x1e,0x1c,0x68,0x78,0xaa,0xeb,0x29,0xae,0xa9,0x69,0x76,0x5d,0x69,0x0,0x6f,0x70,
  0xc0,0xae,0x56,0x4d,0x1d,0x28,0xe5,0xd2,0xe8,0xa4,0xd2,0xf0,0xd6,0x2d,0xa0,0x78,
  0x2b,0xa0,0x6,0x6,0x7,0x86,0x8b,0x83,0x3,0x83,0x3,0xc5,0xe1,0xa5,0xd9,0xb7,
  0x3,0xf2,0x39,0xd5,0x45,0x43,0xd4,0x7f,0x95,0x8d,0xc1,0x1,0x45,0xd1,0x54,0xfd,
  0xea,0xb2,0x6b,0x37,0xeb,0x46,0x41,0xb7,0x2d,0xdb,0xad,0x28,0x43,0xe5,0xf2,0xc4,
  0xf8,0xf4,0xd4,0x4c,0x71,0x58,0xd9,0xc0,0xc4,0x23,0x73,0x8b,0xf3,0x68,0x6e,0x53,
  0x41,0x22,0x20,0x8f,0xed,0x1a,0x0,0x52,0x36,0x6c,0xcb,0x34,0x94,0xa1,0xa9,0x12,
  0x7a,0x66,0xd0,0xc,0x16,0x31,0xa4,0x95,0xd1,0x3,0x47,0x36,0x39,0x10,0x4d,0xcf,
  0xb3,0xeb,0xaf,0xc3,0xe5,0x9c,0x77,0x4c,0x63,0x19,0x78,0xbf,0x27,0x33,0x4b,0xeb,
  0xd,0xf,0xd4,0xf0,0x3c,0xc4,0xd6,0xb9,0xbb,0xf3,0xb6,0xb9,0xbc,0xe2,0x51,0xc,
  0xbd,0xad,0x97,0xfe,0xfd,0x27,0xed,0x97,0x27,0xdd,0xcf,0xf7,0xfd,0x7f,0x7f,0xe3,
  0xdf,0xf9,0xbe,0xdd,0xfa,0xf9,0x97,0x17,0x3b,0x8e,0x5a,0xaf,0x3,0xab,0x77,0x73,
  0xa7,0xf7,0xf0,0x71,0xf7,0xef,0x37,0xfc,0x9d,0xed,0xde,0xdd,0x83,0x6e,0xeb,0x46,
  0xfb,0xe8,0xab,0x24,0x3d,0x63,0x2,0x1e,0x8c,0xcc,0x20,0x9e,0xab,0xd6,0x1b,0x8e,
  0xea,0x82,0xba,0x17,0xab,0xa4,0x48,0xc5,0xb4,0xf,0x6f,0x63,0x10,0xa7,0x7,0xaf,
  0xba,0x2f,0xf,0xd2,0x97,0xbc,0xa0,0x5c,0xbe,0x62,0xdb,0x16,0x9e,0xc0,0x0,0x62,
  0xfb,0xd6,0xed,0x3a,0x98,0xc1,0x43,0xc4,0x37,0x46,0x19,0x8c,0x57,0xab,0xe1,0x60,
  0x16,0x54,0x44,0xe3,0xa8,0x86,0x61,0xd6,0x97,0x2b,0x4a,0x49,0x29,0x3b,0x6b,0x18,
  0x3f,0x44,0xbf,0x62,0x7f,0x0,0x5c,0xa5,0xfb,0xc9,0xcf,0xfe,0xb5,0xeb,0x4a,0x1f,
  0x10,0x2b,0x98,0x71,0x43,0x6,0x60,0x68,0xc,0x4c,0x1a,0xda,0x24,0x5e,0x27,0xaf,
  0x4c,0xc7,0x5,0x8d,0x6,0x30,0x38,0xed,0x47,0x9d,0x35,0x12,0x61,0x19,0x5a,0xf,
  0x8d,0x96,0xc7,0xcb,0x53,0x5a,0x7f,0x8b,0xea,0x2b,0x40,0xbf,0xa,0x8c,0x14,0x55,
  0x68,0xa9,0xa1,0xd3,0xfd,0xbd,0xed,0xd3,0xdd,0xbd,0xce,0xa7,0x5f,0x44,0x91,0x16,
  0x18,0xaf,0x38,0x7c,0x9,0xd4,0x9b,0x58,0xf0,0x5b,0xb6,0xd3,0x74,0xfa,0xb0,0xe8,
  0xbb,0xe,0x62,0xb8,0x64,0x1b,0xe0,0xf,0xe7,0x46,0xcf,0xbd,0xb7,0xc1,0xb8,0xad,
  0xe0,0xa2,0x5d,0x0,0x6d,0x21,0xf5,0x5d,0x41,0xe1,0x96,0x56,0xfc,0xfd,0xaf,0x3b,
  0x2f,0x76,0x21,0x40,0x8c,0x14,0xc3,0xec,0x6d,0xed,0x77,0x6e,0xfd,0xb5,0x1f,0x4f,
  0x73,0xb8,0x2a,0x95,0x1a,0x5c,0xa7,0xa0,0xe5,0x8b,0x82,0xd0,0x74,0xa,0x32,0xd9,
  0x93,0xcf,0xdb,0x87,0x2d,0x8c,0x22,0x60,0xc3,0xc0,0xfd,0x2f,0x9f,0xb5,0x8f,0xf,
  0x20,0xca,0xde,0xe3,0xbd,0xd3,0xed,0x9f,0x2,0x64,0x58,0xbd,0x30,0x1a,0xfe,0xb7,
  0xa,0x12,0x10,0xf9,0x54,0xfc,0xf2,0x99,0xff,0xfc,0x47,0xa4,0x4b,0xeb,0x6,0xab,
  0x4b,0x17,0xce,0xdc,0xf9,0xb6,0xf3,0xe2,0xa8,0x73,0xf3,0x38,0x44,0x1b,0x80,0x87,
  0x79,0xab,0x73,0x7d,0xb7,0xfb,0x7d,0xeb,0xbc,0xb3,0x6,0x7f,0x9f,0x1e,0xfc,0xb,
  0x65,0x8d,0xdb,0xdb,0x61,0xe2,0xd8,0x7a,0xd9,0xdd,0xbb,0xdd,0xb9,0xf5,0xc3,0x59,
  0xd5,0x29,0x9f,0x7b,0x4f,0x11,0x7,0xd2,0x79,0x2a,0x8e,0xfc,0xbf,0x3d,0xe2,0x82,
  0xb9,0x7d,0xf2,0xd0,0xdf,0xb9,0xde,0xf9,0x61,0xf7,0xf4,0xd5,0xd1,0xe9,0xf3,0x87,
  0x67,0x5d,0xbe,0xc4,0x87,0x31,0x93,0x7d,0xe8,0x3,0xe1,0x72,0xd3,0xd4,0xaf,0xce,
  0xea,0x3a,0x34,0x36,0x77,0x40,0xb1,0x33,0x7d,0xa5,0x66,0x22,0x62,0xde,0x73,0xad,
  0x79,0xbb,0xee,0xa9,0x66,0x1d,0x30,0xb2,0x99,0x89,0x3e,0x44,0x23,0xe1,0xb1,0x94,
  0x25,0x0,0x67,0x55,0xcf,0x76,0xb1,0x39,0x6,0x7,0xfc,0xa7,0xfb,0xfe,0xd,0xe4,
  0x5a,0x9,0x5,0x8a,0xd8,0xad,0x1d,0xff,0xc1,0x13,0x1c,0xf2,0xd1,0xe9,0x14,0x2c,
  0x38,0x38,0xd0,0xf9,0xee,0x23,0xff,0x1f,0x7b,0xfe,0xd6,0xd3,0x6e,0xeb,0x41,0xfb,
  0xf0,0xd3,0xce,0x67,0x3b,0x28,0xa2,0xb6,0xf6,0xe1,0xb1,0x5,0x83,0x3,0xfe,0x86,
  0xf1,0xdd,0xd9,0xfe,0xa9,0x7b,0x7c,0x82,0xf,0x16,0x4c,0xd3,0xfb,0xe7,0x1d,0xff,
  0xd1,0xb,0x14,0x4f,0xbb,0x4f,0xda,0xad,0x6f,0xb1,0x44,0x28,0x2a,0xd2,0x55,0xf5,
  0xc0,0xb2,0xed,0xae,0x43,0x6f,0xc9,0x61,0xf9,0xf7,0x8e,0xc2,0xa0,0xdc,0xfd,0xe8,
  0xf4,0xda,0x56,0x50,0x42,0x14,0xc,0xd5,0xbd,0x5a,0x1e,0xf9,0x4b,0xa3,0x81,0x8b,
  0x9,0x9,0x37,0x36,0x1f,0xd9,0x14,0x1a,0xd0,0x4b,0xba,0xe0,0xe0,0x27,0x28,0x70,
  0x55,0x92,0xc0,0x26,0xab,0x44,0xaa,0xa3,0xe8,0x9,0x2a,0x11,0x42,0xcb,0xd6,0x22,
  0x9b,0x49,0x61,0x95,0xaa,0xad,0x37,0x1b,0x58,0xa4,0xdd,0xf4,0x2c,0xe8,0xe3,0xe8,
  0x24,0xdd,0xe4,0x7d,0x48,0x98,0xb0,0x81,0x7f,0x2d,0xcf,0x32,0xb1,0x97,0xe9,0xf,
  0x89,0x45,0x15,0xa5,0xa6,0xba,0xcb,0x66,0xbd,0xe0,0xd9,0x4e,0x65,0x2c,0xd8,0x4c,
  0xd1,0x90,0x66,0xc3,0x2d,0x58,0xb,0x47,0x39,0xd3,0xbf,0xa5,0xc2,0xf2,0x88,0xb5,
  0x7b,0x30,0x94,0x65,0x74,0xba,0xd0,0x8b,0xa,0x11,0x42,0x33,0x19,0xfc,0x49,0x16,
  0x7b,0x43,0xd5,0xa0,0xf8,0xe0,0x27,0xca,0x6a,0x8f,0x6e,0xf6,0x1e,0xdf,0xf7,0x1f,
  0xed,0xa,0x30,0x60,0x4a,0x69,0x8e,0x4d,0x56,0x33,0xe2,0xf5,0xf1,0xc3,0x6a,0x77,
  0x41,0x5e,0xa0,0x85,0x9a,0x27,0xcb,0xc,0x7d,0x12,0x3d,0x41,0xb8,0x5,0x42,0x28,
  0xce,0x91,0xb9,0x80,0x9c,0xd,0x3d,0x81,0xee,0x7f,0x72,0x3c,0x93,0x2c,0x29,0x50,
  0x98,0x9e,0xce,0x93,0x78,0x8a,0xc3,0x1b,0x34,0x4b,0x14,0xfc,0xb8,0xe,0x8f,0x6a,
  0xae,0xb1,0xb1,0x4,0x65,0x4c,0xb7,0x99,0x8a,0x82,0x3e,0xca,0x5,0xc1,0xa0,0x4f,
  0xa0,0x47,0x86,0x3,0x33,0x6f,0xb2,0x5d,0x41,0x49,0x14,0x85,0x24,0xec,0x97,0x74,
  0xd7,0xb6,0x2c,0x91,0x7d,0x44,0x14,0x72,0x4f,0x45,0xb0,0x44,0x6c,0x8c,0xb3,0x42,
  0x70,0x29,0xc9,0x45,0x22,0x63,0x51,0x62,0x42,0x11,0xf5,0xbb,0xaa,0xeb,0xda,0xab,
  0x57,0xd6,0x1d,0x78,0xfa,0x8d,0x91,0xc3,0x17,0xe3,0xc6,0x27,0x6f,0x61,0xd5,0x34,
  0xbc,0x95,0x40,0x87,0x99,0x3e,0xe5,0x8d,0x73,0xf2,0x2c,0x50,0xed,0x57,0x5c,0x6e,
  0x27,0xa7,0x59,0x82,0x72,0x76,0x22,0xf6,0x97,0x3c,0x15,0x15,0xd3,0x78,0x7f,0x31,
  0xa7,0x5,0x3d,0xc1,0xbb,0xd3,0x23,0xee,0x14,0xe6,0x79,0x7e,0xc3,0x45,0x6,0x80,
  0x9,0x90,0xe8,0x5f,0x4a,0xea,0xcf,0xac,0x98,0xe3,0x30,0x20,0x7c,0xb3,0x8e,0x63,
  0x99,0xba,0xca,0x6e,0x5f,0xe9,0x24,0x93,0xa4,0x57,0x57,0x4c,0xf,0xd0,0x9,0x33,
  0xca,0x97,0xe9,0xdd,0x75,0x42,0x6a,0xe2,0x70,0x4b,0x41,0x90,0xe1,0xd3,0xa0,0xf,
  0x9b,0x48,0x5f,0x26,0xf4,0x68,0x9e,0xc5,0xd8,0x82,0xf8,0x8c,0x6a,0xf5,0xb1,0x5e,
  0x96,0xdf,0x52,0x90,0x6,0x65,0xbc,0x59,0x37,0xd0,0xb0,0x1d,0x1a,0xa8,0x38,0xdc,
  0x68,0x6a,0x3a,0x2c,0xf9,0x60,0x58,0x17,0x1c,0xbb,0x61,0x22,0x86,0x8a,0x12,0x6c,
  0xcd,0x19,0x1c,0x5c,0x38,0xa2,0x84,0xc9,0xeb,0x8a,0xaa,0x71,0xd5,0x29,0x1e,0xe9,
  0xab,0x74,0x64,0x59,0x2b,0x15,0x4f,0xd5,0xd8,0x93,0x3e,0x6c,0x72,0x91,0x5,0x21,
  0xcd,0x88,0xe8,0xc6,0x25,0xce,0x80,0xfc,0xd1,0x18,0x83,0x48,0x1e,0x9b,0x54,0xdd,
  0x10,0x6e,0x98,0x68,0x2c,0xec,0xa,0xb8,0x51,0x94,0x61,0x2a,0xca,0x84,0xb0,0xc6,
  0x88,0x69,0xe1,0x58,0xb8,0xb,0x27,0xc8,0x18,0x69,0x36,0x58,0x1,0x5c,0xb,0x32,
  0x91,0xdc,0xb3,0x94,0x4d,0x2a,0xd,0x60,0x1,0xdd,0x23,0x91,0x26,0x8,0xaf,0xc8,
  0x38,0x11,0x69,0x64,0x25,0xca,0x8,0x74,0x4e,0x41,0x1c,0x24,0x4,0x13,0x3c,0xbc,
  0x65,0xe3,0xfa,0x47,0xa,0x31,0x8,0xe3,0xca,0x6f,0x58,0xa4,0x49,0x41,0xe1,0x55,
  0x48,0x84,0x20,0x4e,0x68,0xbc,0x4,0x2,0x26,0xbc,0x3e,0x13,0x45,0x82,0x8c,0x85,
  0xd2,0x39,0xb1,0xe0,0x62,0xa,0x67,0x22,0x85,0xc7,0xad,0x1c,0xbe,0xd1,0x50,0x28,
  0xdd,0xa3,0x29,0xae,0xd0,0xed,0x9c,0x7c,0xdc,0xfb,0xba,0xd5,0xb9,0x8b,0x2e,0xe1,
  0x80,0xe5,0xdf,0xba,0x77,0x7a,0xed,0x63,0x3d,0x4c,0xe0,0x51,0x6b,0xdf,0x3e,0xbc,
  0x76,0x7a,0xf3,0xd9,0xe0,0x0,0xec,0x57,0x60,0x63,0x4b,0x7a,0x97,0x63,0x2a,0x4a,
  0x51,0xe3,0xf1,0xd9,0x77,0x9d,0x5b,0xf,0x3a,0x7b,0xfb,0xb3,0x3a,0xda,0x9a,0xa8,
  0xe5,0xc7,0x9d,0x32,0xac,0x20,0xe1,0x74,0xfb,0xf0,0x13,0xf8,0xa3,0xb3,0x73,0xc,
  0x7b,0xa0,0xa8,0x5b,0x85,0xfd,0xab,0xff,0xf4,0xb,0xff,0xfe,0xab,0xee,0x37,0xc7,
  0xa,0x5b,0x65,0xf3,0x77,0x6a,0xd2,0x42,0x16,0x97,0x4e,0xd0,0x5e,0x11,0x3,0xb7,
  0xf1,0x52,0x9b,0x12,0x8a,0x4b,0x9e,0xb9,0x93,0x17,0x5d,0x6c,0x68,0x97,0x4a,0x25,
  0x21,0x4,0x42,0xcd,0xdf,0xba,0xb2,0x65,0xd0,0x58,0x58,0xb0,0xd2,0x50,0xa8,0x40,
  0xe3,0x65,0x8,0xf,0x7f,0x91,0x8c,0x45,0x1,0xbf,0x4c,0x2d,0xe6,0x2a,0x2d,0xcb,
  0xb2,0x84,0x3a,0x4d,0xad,0xb8,0xe,0x97,0xa8,0xc5,0xcb,0x10,0xa8,0x25,0x96,0xb1,
  0x28,0xe0,0x97,0xa9,0x45,0x1d,0xb0,0x99,0xbe,0xa2,0x4e,0xb6,0xf4,0x82,0x55,0xaa,
  0x12,0x2b,0x21,0xa5,0x48,0x13,0x2a,0xc4,0x9f,0xac,0xb1,0x42,0xaf,0xab,0x96,0x5,
  0xdc,0xf5,0x8c,0x82,0x3a,0x2e,0x86,0x19,0xfa,0x7e,0x2b,0x69,0x8e,0x99,0x2d,0xa1,
  0x85,0x88,0x98,0x32,0x26,0xad,0xd0,0x97,0xe2,0x62,0x9b,0x90,0xb4,0xea,0x56,0x88,
  0x2e,0x59,0xd6,0xd2,0x59,0x31,0x64,0x61,0x5b,0x95,0x70,0x30,0x3f,0xe8,0x5c,0x66,
  0xe4,0xfa,0xbe,0x88,0x35,0xad,0xe5,0x23,0x44,0x5c,0xab,0x92,0xc0,0x1f,0xf4,0xb0,
  0x42,0x25,0x82,0x19,0xac,0x49,0x63,0xc5,0x5e,0x2d,0x18,0x40,0xb7,0xdd,0xa0,0x9e,
  0x2a,0x90,0x3,0x3,0xea,0x26,0x29,0x66,0xc5,0x90,0xc3,0x86,0x59,0x8a,0x3b,0xaf,
  0xd5,0x92,0x8d,0x77,0x62,0xb7,0xd2,0x94,0x95,0xa,0xac,0xc2,0x6b,0x5c,0xfd,0x90,
  0x27,0x1e,0x18,0xbc,0x71,0x81,0xc0,0x0,0xd7,0x2c,0x28,0x28,0xc1,0x23,0xe0,0xc8,
  0x82,0x97,0x2b,0xdc,0xd3,0x91,0x47,0x4e,0x66,0x16,0xf9,0xb3,0x9,0x56,0x1d,0xdb,
  0xf5,0x84,0x5e,0x26,0x93,0xf2,0x4b,0x9f,0xe0,0x2f,0x59,0xfa,0xa2,0x83,0x98,0x95,
  0x88,0x46,0x44,0x99,0x7e,0x3,0x4d,0x8c,0xe4,0xce,0x14,0x1,0x35,0x1b,0x23,0x2,
  0x7b,0x2c,0xcc,0xa3,0x27,0xa6,0x4f,0x8b,0x5,0x44,0x81,0x8d,0x8c,0xe1,0x45,0x77,
  0xcf,0xb0,0xe0,0xa4,0xff,0xe5,0x88,0x65,0xa9,0xe4,0x1c,0xd1,0x45,0xce,0x1d,0x46,
  0xc3,0x94,0x18,0xa1,0x84,0x4b,0xe,0x9c,0xcc,0x38,0x66,0x56,0x12,0x5d,0xc9,0x8,
  0x77,0x19,0x6f,0x51,0x3e,0x11,0x72,0x0,0x75,0x72,0x7a,0x30,0x4d,0x2,0x75,0x1,
  0x81,0xc2,0x86,0x5c,0xfa,0x5d,0x7e,0x3,0x76,0x6c,0x17,0xd,0x13,0xdd,0x8c,0x76,
  0xb7,0x8f,0x3a,0x3b,0xf7,0x44,0x37,0x90,0x17,0x94,0x88,0x4e,0xba,0x23,0xe6,0x4b,
  0xf3,0xe5,0xf9,0xf1,0x44,0x8f,0x33,0xb4,0xb0,0xb0,0x10,0xc,0x62,0xc3,0xa2,0x74,
  0x25,0x30,0xd1,0xf4,0xdc,0xdc,0xdc,0xc2,0x14,0x47,0xc7,0x9a,0x76,0x33,0x5,0x14,
  0xb6,0x88,0xfc,0xb5,0x64,0x79,0x72,0x5a,0xab,0x8e,0xc7,0x9b,0x26,0xd2,0x7e,0x1e,
  0x95,0x17,0x73,0xf6,0x1a,0xa3,0x7d,0x62,0x19,0x42,0xb5,0x41,0xdf,0x6d,0x46,0x19,
  0x52,0xe0,0x73,0xe1,0xdb,0x8e,0x78,0x51,0xbb,0xa6,0xd9,0xd9,0x8b,0x86,0x54,0xf2,
  0x14,0x54,0x36,0x4a,0x46,0x55,0x12,0x74,0xe4,0xa6,0x43,0x64,0x35,0x22,0x39,0x23,
  0xc5,0x45,0x46,0xe3,0x83,0x3c,0x55,0x26,0x80,0xee,0x50,0x35,0xb,0x64,0xed,0xe,
  0xfa,0x32,0xe6,0xd7,0x8,0x8d,0x8,0x50,0xc5,0x70,0x6d,0xa7,0x60,0xd8,0xab,0xe1,
  0xae,0xa0,0xee,0x17,0x6c,0xd8,0xe8,0x9a,0xf5,0xa,0x49,0x40,0x33,0xdc,0x74,0x7c,
  0xfd,0x0,0x3b,0xf2,0xf0,0xa,0x22,0xbe,0x80,0x50,0x46,0x41,0x4d,0x74,0xf7,0x9e,
  0x13,0x4c,0x86,0xf1,0x17,0x5e,0xbb,0x78,0x71,0x6e,0x4c,0x96,0x51,0xa6,0x81,0x51,
  0xae,0x4e,0x67,0x2f,0x6,0xd7,0x29,0x4,0x17,0x94,0x78,0x1d,0xb3,0xa6,0x2e,0x83,
  0x8a,0xd2,0x74,0xad,0xdf,0x56,0x8a,0x84,0xaf,0x18,0x8c,0x16,0x61,0xfb,0x60,0x37,
  0x5d,0x1d,0x14,0x67,0x11,0xfd,0x6b,0x90,0x73,0xc4,0xa9,0x2f,0xff,0x2e,0x79,0xd0,
  0x70,0x2f,0x41,0xd8,0x74,0x21,0x7c,0x43,0x52,0x1c,0x4e,0x4f,0xe2,0xb2,0x43,0x88,
  0x8a,0x61,0x6,0x40,0xf0,0x5,0xa,0xdb,0x5,0x53,0x8,0xb8,0xd9,0x7c,0x57,0x3f,
  0xfc,0x85,0xe0,0x66,0x9a,0xc8,0x8c,0x3b,0x2f,0xf4,0xc2,0xf6,0xf9,0x8f,0xe8,0xed,
  0x12,0x79,0xfd,0xdd,0x7d,0xda,0xea,0xb6,0xbe,0xa2,0xbf,0x35,0x21,0x1f,0x23,0x98,
  0xcc,0xe5,0x25,0xbf,0xd0,0xd0,0xd2,0x2c,0xa4,0x30,0x6b,0xe6,0x87,0xe0,0x1d,0xb3,
  0xe,0xbd,0x29,0x51,0x29,0xa,0x54,0x1d,0x6a,0x3,0x5c,0x3e,0x6a,0x5c,0xe0,0x0,
  0xd5,0x43,0x10,0xc3,0x9f,0x3c,0x41,0xae,0xb8,0xb8,0x62,0x7a,0x16,0xd0,0x54,0xf7,
  0x7d,0x8,0xe9,0xfd,0x3f,0xa2,0xe0,0x1d,0x69,0x7c,0x40,0x2,0xa4,0x4f,0xd,0xa8,
  0x7c,0x2d,0x88,0x6e,0xed,0x3c,0x7a,0xce,0x26,0x98,0xfe,0x42,0x45,0x74,0x14,0xab,
  0xf0,0xd1,0x88,0x9f,0x2e,0xa9,0x6b,0x59,0xe6,0x57,0xd7,0x24,0xe6,0xcf,0x74,0x40,
  0xe,0x17,0xfc,0x37,0x4e,0x50,0xd7,0xfa,0x72,0x82,0x40,0x8f,0xcc,0x6f,0x43,0xf2,
  0xfa,0x41,0x24,0x5b,0xfe,0xd1,0x4e,0x7f,0x7a,0xbe,0x69,0xbb,0x35,0xd5,0xa2,0x54,
  0x55,0x22,0x13,0x47,0xb5,0xcc,0xbc,0x65,0x37,0x40,0x86,0x23,0x3,0x9a,0xff,0x3b,
  0x2f,0x6,0xa8,0xfa,0xf1,0x63,0x42,0x8d,0x8c,0x97,0xb,0xe0,0xfc,0xe8,0x68,0x79,
  0xec,0xc,0x62,0xb3,0x3f,0xab,0xa9,0x8e,0x4e,0x95,0xa6,0x54,0xe2,0x87,0xff,0x0,
  0x2,0x41,0x4d,0xbf,
	// F:/src/SARibbon/src/SARibbonBar/resource/theme-office2021-blue.qss
  0x0,0x0,0x8,0x3c,
  0x0,
  0x0,0x25,0xf0,0x78,0x9c,0xcd,0x59,0x5b,0x6f,0xdc,0x44,0x14,0x7e,0x8f,0x94,0xff,
  0x60,0xc8,0xb,0xac,0xb4,0xd9,0x4b,0xb2,0x9,0x75,0x4,0x52,0xb2,0x34,0x7d,0xe0,
  0x4e,0x2a,0xfa,0x80,0x10,0xf2,0xda,0xb3,0x89,0x55,0xaf,0xc7,0x78,0xbd,0x24,0x25,
  0x8a,0x54,0x8,0xd0,0x6,0x42,0x53,0x10,0x94,0x14,0x15,0xd4,0x16,0x28,0x11,0xa8,
  0xad,0x44,0x11,0xa,0x49,0xdb,0xfc,0x99,0xb5,0xb3,0xfb,0xd4,0xbf,0xc0,0x8c,0xaf,
  0x33,0xe3,0x19,0xdb,0x69,0x28,0x6a,0x46,0x95,0xb6,0x33,0x67,0xce,0x7c,0xe7,0x3a,
  0xe7,0x8c,0x1f,0xed,0x1f,0x54,0x4a,0xa3,0x23,0xd2,0xe0,0xe0,0xaa,0xb7,0x7d,0xd7,
  0xdb,0xb9,0xd1,0xdf,0x3f,0x80,0xed,0xb6,0xae,0x82,0x7a,0xb5,0x36,0x75,0xf8,0xc3,
  0xa7,0xfd,0xdd,0xfd,0xe1,0xcd,0x6d,0xb4,0xe0,0xfd,0x78,0x30,0x3a,0x52,0xaa,0x8c,
  0x8e,0x8c,0x8e,0x54,0x4a,0xb,0xb3,0x6f,0xeb,0xad,0x16,0x34,0x67,0x2d,0xcb,0xd0,
  0x55,0xc5,0xd1,0xa1,0x79,0x46,0xd7,0x16,0x81,0x83,0x9,0x84,0x8b,0xab,0xe8,0x20,
  0xf4,0xd7,0x52,0xd4,0xb3,0x8b,0x36,0xec,0x99,0x5a,0x59,0x85,0x6,0xb4,0x65,0x69,
  0xc,0x34,0xc0,0x4,0x68,0xcc,0x54,0x4a,0xd2,0x6a,0xb0,0x77,0x7c,0xee,0x95,0x26,
  0x5e,0x5b,0x93,0x30,0x47,0x7f,0x17,0xb4,0x35,0x80,0x68,0xbb,0xd0,0xd0,0xb5,0x78,
  0x47,0xb0,0x16,0xb0,0x19,0xab,0x4f,0xe2,0x81,0xe6,0xd6,0x68,0x94,0x73,0x8a,0x4d,
  0xe2,0x42,0xff,0x95,0x7c,0x28,0x8f,0x3,0x44,0xc,0x23,0xf,0x44,0xcf,0x71,0xa0,
  0x79,0xa,0x1d,0x67,0xa5,0x55,0x95,0x5a,0x14,0x1,0x74,0x6c,0xc5,0xec,0x5a,0x8a,
  0xd,0x4c,0x27,0x3a,0x82,0x3e,0xa6,0xe9,0xd8,0x46,0x13,0x9a,0x8e,0xa2,0x9b,0x80,
  0x92,0x9a,0x5a,0x38,0x2,0x7b,0xec,0x1e,0x11,0x8f,0x5,0x80,0xd6,0x14,0x7,0xda,
  0x1,0xc8,0xd1,0x11,0xef,0xde,0x8e,0xf7,0xe9,0xd6,0xa3,0xfb,0x9b,0x2,0xa,0xe4,
  0x40,0x83,0xf5,0x4d,0xef,0xea,0xdd,0xe1,0xcd,0x6b,0x83,0x8d,0x3f,0x91,0x8b,0xf5,
  0x1f,0x1c,0x1c,0x7e,0xbb,0xe3,0x1f,0x37,0x3a,0xe2,0xde,0xfa,0xc4,0xfb,0xe9,0x9a,
  0xb7,0x7e,0xef,0x70,0xff,0x6a,0x7f,0xf7,0x2b,0xf7,0xf2,0x26,0xda,0x30,0x5c,0xdf,
  0xe9,0xef,0xff,0xdd,0xdf,0xfd,0x12,0xfd,0x76,0x2f,0x7e,0xee,0x6e,0xfc,0x79,0xb8,
  0x77,0x30,0xb8,0xf3,0xf0,0xf0,0xc1,0x9d,0x80,0x66,0xf8,0xf3,0x25,0xef,0xfa,0x7d,
  0x74,0xa8,0xbb,0x75,0xb7,0xbf,0xff,0x6b,0xc0,0x11,0xb1,0x8a,0x25,0x55,0x1c,0xb0,
  0x8,0xed,0x73,0xd2,0x4b,0x92,0x18,0x96,0x77,0xe5,0x1f,0xf7,0xfe,0x96,0xcf,0xe4,
  0x93,0xc1,0xf9,0x75,0x67,0x9,0x74,0x40,0x59,0x53,0xec,0xb3,0xf5,0xf1,0xf,0xba,
  0xdd,0xc0,0xd3,0x5,0xbb,0x3,0xe5,0x45,0x1e,0xd3,0x2,0x6a,0x55,0xad,0x7,0xda,
  0x22,0xcc,0x10,0x62,0xa0,0x2c,0x10,0xce,0xc9,0x6d,0xa8,0xf6,0xba,0x1,0x17,0xd8,
  0x73,0xc,0x64,0x10,0x59,0x32,0xa1,0x9,0x22,0x95,0xa7,0x4,0x11,0x79,0x6b,0xdb,
  0xff,0xf3,0xbd,0x35,0xa2,0xa5,0xfd,0x35,0x65,0xc1,0x88,0x2c,0x50,0xef,0x93,0xb2,
  0x2b,0x4f,0x6a,0xb1,0x35,0x4,0xfa,0x94,0xa4,0x8e,0x62,0x2f,0xea,0x66,0xd9,0x81,
  0x96,0x3c,0x61,0xad,0x90,0x53,0x2d,0x88,0xe2,0xa5,0x13,0xce,0x32,0x81,0xf6,0xa6,
  0x62,0x2,0x23,0x48,0x54,0xd4,0x54,0x9e,0x16,0xc9,0xe8,0xe,0xad,0x91,0xa0,0x9a,
  0xf2,0xff,0x58,0x3,0x5,0x7c,0x13,0xc1,0x44,0x61,0x1c,0x72,0xad,0x59,0x2b,0x51,
  0xde,0x98,0xaa,0xe2,0xe1,0x5b,0xce,0x67,0x42,0xec,0x1c,0x9f,0xf3,0xc9,0xd3,0x56,
  0xa4,0x4e,0x7d,0x55,0x69,0xa1,0xa3,0xfd,0x9f,0xc8,0x96,0xde,0xf5,0xb,0x38,0x4b,
  0x5f,0xdf,0xe2,0xc8,0x1d,0x50,0xa,0x73,0x2f,0x1d,0xf2,0x49,0x36,0x65,0x65,0x66,
  0xce,0x7f,0xc3,0xc2,0x79,0x3d,0x80,0xcd,0x39,0x94,0x5c,0x2e,0x92,0x6c,0x2a,0xa5,
  0x55,0x72,0x4b,0xec,0xc5,0x41,0xd2,0x8d,0x10,0x4d,0x4c,0x4c,0xa4,0x28,0x13,0xba,
  0xb4,0x69,0x48,0x42,0x79,0x9,0x7e,0x28,0x4e,0x7c,0x63,0x6a,0x3,0xf,0x11,0x8e,
  0x60,0xf3,0x1a,0x7d,0x5,0x54,0x13,0xef,0x4b,0x65,0xe1,0xd0,0xe9,0x17,0x54,0x1b,
  0x1a,0x6,0x4f,0x4b,0x3c,0xa,0xa1,0xb7,0x24,0xe0,0x78,0xdb,0x28,0x87,0x9,0x21,
  0x72,0xbc,0xbc,0x86,0x87,0x98,0xc7,0x2b,0x2,0x45,0xf2,0xa8,0xdf,0x55,0x6c,0x1b,
  0x2e,0x9f,0x3e,0x67,0x81,0x17,0x9f,0x9d,0x78,0xf6,0x3d,0x12,0x77,0xd9,0xd6,0x17,
  0x97,0x9c,0xf2,0xb2,0xae,0x39,0x4b,0xbe,0xc,0xa2,0xa4,0x26,0xe2,0x37,0xc9,0xf0,
  0x33,0x40,0xfb,0xa8,0xec,0xa,0x9b,0x3a,0x4b,0x13,0x84,0xc9,0xd7,0x58,0xf3,0x2e,
  0x38,0x88,0x2b,0xd0,0xd2,0xf7,0x38,0xb5,0xc0,0x9a,0xd3,0x89,0xcc,0xc9,0x4d,0xdb,
  0x6c,0xd0,0xc7,0xa,0x40,0xe9,0x2f,0x92,0xbf,0x9a,0x96,0x9f,0x3a,0x31,0xf7,0x66,
  0xe1,0x56,0x6e,0x89,0x7b,0xa,0x17,0x29,0x51,0x52,0xc9,0x31,0x2e,0x79,0xf2,0xa3,
  0x5c,0x5a,0x4d,0xf1,0x66,0x2b,0x2c,0x4b,0xd1,0x34,0xdd,0x5c,0xf4,0xd,0x1f,0x89,
  0x9c,0xcc,0xfa,0xee,0x95,0x9e,0xc6,0x77,0x84,0x54,0x6e,0x30,0xb3,0xe1,0x35,0x11,
  0x2d,0x50,0x8a,0x4b,0xe1,0x20,0xbd,0x6,0x1,0xed,0xef,0x7e,0x81,0x2f,0xc9,0xdb,
  0x1b,0xc3,0x3f,0xb6,0xbd,0x2b,0x17,0xdc,0xdb,0xdf,0xa3,0xea,0xe3,0xf0,0xb7,0x7d,
  0xf7,0x8b,0x9d,0x94,0x7d,0xe4,0x46,0x1c,0xab,0x6c,0x3a,0xd,0x89,0x22,0x24,0x9,
  0xe1,0x98,0x56,0x55,0x81,0x56,0x9b,0x61,0x43,0x27,0x51,0x2f,0x11,0x0,0x32,0xaf,
  0x3c,0x48,0x4b,0x60,0xd9,0xa0,0xdb,0x5,0xda,0xff,0x27,0x43,0x7d,0x7a,0xaa,0xaa,
  0x4c,0xff,0xa7,0x32,0x14,0x2d,0x8e,0xd2,0x3b,0xe5,0xe,0x30,0x7b,0x65,0xdd,0xd4,
  0xf0,0x34,0x8c,0x2d,0xd9,0xed,0xb5,0x54,0x54,0xf7,0xa2,0x8,0x2f,0x5b,0xb0,0xab,
  0xe3,0xd,0xb2,0xe4,0xc3,0x9c,0x9,0x74,0x10,0x4,0x57,0x95,0x57,0x4b,0x9c,0x56,
  0x5a,0x4c,0xf3,0x10,0xcc,0x1c,0xa9,0x3c,0xa7,0xb7,0xca,0xb2,0xa3,0xb4,0xe8,0x92,
  0x27,0x8c,0x1f,0x1c,0x1d,0x88,0x66,0x9c,0xdb,0x6f,0x24,0x1a,0x8d,0xf,0x4d,0xdf,
  0xdd,0x44,0xc1,0x14,0x47,0x48,0x38,0x17,0xc6,0x4d,0x83,0x9e,0xd,0x62,0xac,0xc1,
  0x2d,0xae,0x12,0xe,0x68,0x2e,0x4c,0x40,0xd,0x36,0xee,0x1e,0x23,0x48,0x27,0x45,
  0x41,0x3a,0x99,0xe,0x52,0x42,0x65,0x72,0x17,0x18,0x40,0x75,0x22,0xef,0x8e,0x75,
  0x17,0x78,0x20,0xa5,0xbb,0x98,0x34,0x52,0x62,0xa5,0x34,0xbc,0xf1,0x37,0xea,0x2b,
  0x6,0xb7,0x3e,0xf6,0x3b,0x8d,0xf3,0xfd,0xdd,0xdf,0x87,0xe7,0xb7,0xbc,0xed,0x4b,
  0xa8,0x90,0xd,0x34,0x8c,0x22,0xc4,0xdd,0xbb,0x85,0xfb,0x81,0xcb,0xb7,0xdc,0x8b,
  0x57,0xc3,0x68,0xb9,0xff,0x83,0x7b,0xf9,0x6b,0x14,0x45,0xee,0xc7,0x5b,0x28,0x6c,
  0x52,0x21,0x33,0x59,0x34,0x64,0x26,0x39,0x21,0x23,0x14,0xd4,0xcf,0x43,0xf2,0x33,
  0xb4,0xbc,0x4f,0x93,0x8,0x44,0xe6,0xca,0x72,0x61,0x56,0x10,0xa2,0x98,0xa5,0x62,
  0xc,0x42,0xa2,0x42,0x42,0x1c,0x8,0xad,0xc4,0x4b,0x4c,0xc3,0xe1,0x1e,0x7c,0x36,
  0xbc,0xb1,0xef,0x7e,0xb3,0x69,0xe1,0x32,0xcf,0xbb,0x78,0x65,0x70,0xfe,0x33,0x35,
  0xbc,0x4a,0x71,0xb,0xe9,0x13,0x21,0x25,0xd,0x2e,0xfc,0x35,0x3a,0x82,0x72,0xde,
  0x60,0xfd,0x41,0xd4,0x41,0xee,0x11,0x72,0xc6,0xba,0x72,0xaf,0xed,0xcc,0xaa,0x38,
  0x33,0xbc,0x86,0x52,0x88,0xb7,0x73,0x3,0xb5,0x86,0x28,0x71,0xa2,0xe5,0x20,0x83,
  0xba,0x9b,0x7b,0xa8,0x13,0xc5,0x5d,0xe3,0xe6,0xc6,0xf0,0x9b,0x3b,0xde,0xf7,0xd7,
  0xbd,0x7b,0xdf,0x79,0xdb,0xf,0xf,0x7f,0xd9,0x93,0xe8,0x6e,0x27,0x41,0x4c,0x17,
  0x45,0x8a,0xa6,0xf7,0xba,0x72,0xe4,0xfa,0x9c,0x12,0x8f,0x68,0x42,0x78,0x3a,0x8d,
  0xd9,0x32,0xd9,0x21,0xb3,0x27,0x24,0x76,0xa5,0xbb,0xc2,0x34,0x62,0xfa,0xfa,0x38,
  0xe,0xf2,0x6a,0xb5,0xca,0x85,0x1d,0x9d,0x90,0x1,0x1f,0xd4,0xf0,0x10,0xc0,0xa7,
  0xf7,0x8b,0xc4,0x20,0xab,0xbf,0x63,0x8,0xc1,0x47,0x42,0xd6,0x86,0x19,0x46,0x68,
  0xb4,0xa7,0xda,0x53,0x22,0x29,0x88,0xdd,0x22,0x19,0xd4,0x25,0x80,0xcb,0x3a,0x61,
  0x47,0xd0,0x68,0xe3,0x91,0x3a,0x20,0x29,0x22,0x23,0xe,0x4c,0x31,0xc9,0x68,0xa2,
  0x80,0xab,0xa5,0x18,0x71,0x6c,0xd6,0xc2,0x43,0x24,0x2d,0xb5,0x3f,0x47,0xde,0xb4,
  0xed,0x9e,0x62,0xa9,0x33,0x3d,0x35,0x2d,0x75,0x22,0xf7,0x29,0xc5,0x30,0x80,0x7d,
  0x2e,0xa7,0xe9,0x4b,0x1a,0x36,0x8a,0xfe,0xa8,0xdd,0x1e,0xb3,0x99,0x6e,0xf3,0xb8,
  0x88,0x72,0x8c,0x90,0xb4,0x4f,0x42,0x5c,0x74,0xbb,0x9c,0xd5,0x81,0x71,0xd1,0xb1,
  0xad,0x57,0x72,0x57,0x84,0xe4,0x74,0x2b,0x1d,0x4e,0x16,0x7,0x5c,0x48,0x85,0xcc,
  0xeb,0x44,0xbc,0x35,0xeb,0x61,0x22,0x22,0x62,0x5a,0xe9,0x14,0x7e,0xff,0x9d,0x87,
  0x2b,0x84,0xbf,0x12,0x48,0xd2,0x5d,0x82,0xcb,0x65,0xd,0xa8,0xd0,0xf6,0x8b,0xdc,
  0x72,0x74,0x8d,0x22,0xd9,0xa,0xb5,0x5a,0xab,0x24,0xcb,0xc,0xdc,0x45,0xb5,0x96,
  0x7e,0x9c,0x4a,0x5,0x34,0x49,0x29,0xcb,0xba,0x3,0x3a,0x4c,0xd5,0x56,0xc4,0x17,
  0x28,0xbc,0x49,0x29,0x47,0x1,0x6f,0x19,0x88,0x51,0x6a,0xf,0x67,0x47,0x1e,0xbc,
  0x42,0xae,0x9e,0x8d,0x3c,0x36,0x32,0x75,0xc8,0x3b,0x3a,0x58,0xb6,0xa0,0xed,0x70,
  0xad,0x1c,0x2d,0xe6,0x3f,0x49,0x32,0xfe,0x83,0xcb,0x13,0x9a,0x23,0x9e,0xa1,0x2b,
  0xe2,0xd0,0x17,0xf1,0xc2,0x78,0xe1,0x2c,0xe1,0x53,0xd3,0x3e,0xc2,0xd1,0xc7,0x7c,
  0x13,0x8f,0x84,0x3e,0xcb,0x17,0x30,0x45,0xa0,0xe4,0x0,0x5e,0x58,0xee,0x7,0xed,
  0x23,0xf1,0xaf,0x80,0x2f,0xb,0x39,0x17,0xf0,0xae,0x29,0x3c,0x58,0x9,0x33,0x7c,
  0x84,0x60,0x4e,0xf8,0x6,0x59,0xd8,0xe4,0xfa,0x31,0x75,0x12,0xef,0xe1,0x90,0x1b,
  0x65,0xac,0x46,0xd9,0x24,0xc8,0x0,0x54,0xa3,0x9b,0x83,0xea,0xe4,0x6a,0xe4,0x93,
  0x64,0xa5,0x14,0x3d,0xf,0xbf,0xf5,0x2a,0x6a,0xa3,0x4f,0x6a,0x3a,0x7e,0xb7,0x3f,
  0xdc,0xf8,0xc7,0xdd,0xbc,0x42,0xd6,0xac,0xd1,0x2b,0x76,0x4c,0x25,0x8c,0x87,0x66,
  0xb5,0x59,0x6f,0xb2,0xf,0x3d,0x48,0xf6,0xf9,0xf9,0x79,0x7f,0x32,0x50,0x2b,0x4e,
  0x56,0x1c,0x5,0x9d,0x98,0x9b,0x9b,0x9b,0x9f,0x66,0xe8,0x68,0xc5,0xae,0x65,0x80,
  0xa,0xf4,0x11,0x3d,0x63,0xa7,0xb1,0xd5,0xa7,0x4e,0xb4,0xda,0xc4,0x77,0xb7,0x58,
  0xf6,0x26,0xbe,0x89,0xe7,0xe0,0x4a,0xb6,0xec,0x11,0xd5,0x2a,0xff,0x8b,0x22,0xf7,
  0xed,0x9c,0xfb,0xb5,0x2c,0x39,0x17,0x76,0x5a,0x30,0xff,0xdc,0x90,0x4a,0x9c,0x83,
  0xea,0x5a,0x55,0x6b,0xb,0xbc,0x6e,0x79,0x9,0x79,0xaa,0x40,0x71,0x11,0xe7,0x9c,
  0x1c,0x17,0xeb,0x8d,0xf5,0xf2,0x4c,0x9e,0x0,0x59,0x44,0x69,0x19,0x20,0x2f,0x3c,
  0x12,0x88,0x4f,0xc6,0x3b,0x62,0x40,0xb2,0x66,0x43,0xab,0xac,0xc1,0xe5,0x30,0x2c,
  0x88,0x57,0x1f,0x68,0xeb,0x28,0x42,0xe4,0x28,0x3,0xcd,0x30,0xcb,0xc9,0xa3,0x10,
  0xea,0x87,0xc3,0x87,0xa1,0xe4,0x59,0x48,0xaa,0x81,0xe,0xef,0xd3,0x50,0x41,0x30,
  0x39,0xca,0x9f,0x7f,0xf9,0xe4,0xc9,0xb9,0x9,0x51,0x4a,0x39,0x1,0xb4,0x7a,0xfb,
  0x44,0xfe,0x61,0xe8,0x9c,0xb2,0xff,0x82,0x1e,0x9c,0xa3,0x77,0x94,0x45,0x20,0x4b,
  0x3d,0xdb,0x78,0x4e,0xae,0x44,0xfb,0x2a,0xfe,0x6c,0x5,0xf5,0x4f,0xb0,0x67,0xab,
  0xa0,0x32,0x8b,0xe9,0x5f,0x46,0x3b,0xc7,0x2d,0x73,0xf1,0xf9,0x19,0xe6,0xf3,0x25,
  0xf3,0x85,0x8e,0xbe,0x72,0xb8,0x9f,0xef,0x2a,0xa5,0xec,0x1c,0x2e,0xba,0x83,0x8,
  0xf,0xa6,0x0,0x9c,0xeb,0xa2,0x14,0x4c,0x3f,0xd,0x10,0x8,0x98,0xd5,0x62,0xcf,
  0x71,0xec,0x3,0xf6,0x5a,0x16,0xcb,0x9c,0x77,0xc8,0x4a,0xe9,0x35,0x9d,0x7a,0x32,
  0x67,0xf7,0x8f,0x2d,0xcc,0x22,0xa,0xbd,0xa3,0x7f,0x4,0xce,0xe8,0x26,0x32,0x91,
  0x0,0x69,0xec,0x7d,0x2a,0x2,0x9,0x6c,0xd6,0x15,0x6c,0x60,0x1,0xc5,0xc1,0x27,
  0x87,0x3f,0x59,0x82,0x42,0xc6,0x3e,0xad,0x3b,0x6,0x68,0x29,0xf6,0xfb,0x8,0xd2,
  0x78,0xf7,0xc3,0xc8,0xde,0x47,0x46,0x4f,0xe4,0x60,0x71,0xd3,0x4b,0xb9,0x6b,0x51,
  0xc6,0x61,0x63,0x2f,0xbc,0xc6,0x15,0x34,0x5a,0x33,0x18,0x72,0xa0,0x7c,0x65,0x25,
  0x4f,0xf9,0xca,0x8a,0x40,0xf9,0xb9,0xea,0x2f,0x60,0x80,0xe3,0x98,0x40,0x59,0x21,
  0x4c,0x70,0x44,0x9,0xc8,0x74,0x72,0x2c,0x1b,0xf0,0x78,0x53,0x6f,0xe,0xc7,0x90,
  0xf0,0x75,0x68,0x77,0x14,0xc3,0x17,0x52,0x8a,0xd5,0x1a,0xd7,0x24,0x4d,0x3,0x76,
  0x41,0x8e,0xf1,0x7c,0x9a,0xa7,0xce,0x72,0x3e,0xaa,0x62,0xb6,0x4b,0x9,0x90,0xf3,
  0x49,0x12,0xbc,0x50,0xab,0xd5,0x27,0x1e,0x83,0x2d,0xf5,0x64,0x27,0xa8,0xf1,0xa7,
  0xab,0xd3,0x4a,0x64,0x81,0x7f,0x1,0xe7,0x6b,0x15,0xd9,

};

static const unsigned char qt_resource_name[] = {
  // SARibbonTheme
  0x0,0xd,
  0xc,0xeb,0x1c,0x55,
  0x0,0x53,
  0x0,0x41,0x0,0x52,0x0,0x69,0x0,0x62,0x0,0x62,0x0,0x6f,0x0,0x6e,0x0,0x54,0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,
	// SARibbon
  0x0,0x8,
  0x6,0x8f,0x83,0xbe,
  0x0,0x53,
  0x0,0x41,0x0,0x52,0x0,0x69,0x0,0x62,0x0,0x62,0x0,0x6f,0x0,0x6e,
	// image
  0x0,0x5,
  0x0,0x70,0x37,0xd5,
  0x0,0x69,
  0x0,0x6d,0x0,0x61,0x0,0x67,0x0,0x65,
	// resource
  0x0,0x8,
  0xc,0xa6,0xc7,0x95,
  0x0,0x72,
  0x0,0x65,0x0,0x73,0x0,0x6f,0x0,0x75,0x0,0x72,0x0,0x63,0x0,0x65,
	// ArrowUp.png
  0x0,0xb,
  0xc,0x52,0x65,0x87,
  0x0,0x41,
  0x0,0x72,0x0,0x72,0x0,0x6f,0x0,0x77,0x0,0x55,0x0,0x70,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
	// Titlebar_Max.svg
  0x0,0x10,
  0x6,0xec,0x88,0x7,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x4d,0x0,0x61,0x0,0x78,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// Titlebar_Min.svg
  0x0,0x10,
  0x6,0xa6,0x88,0x7,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x4d,0x0,0x69,0x0,0x6e,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// ArrowMore.png
  0x0,0xd,
  0x7,0xb6,0x8e,0xe7,
  0x0,0x41,
  0x0,0x72,0x0,0x72,0x0,0x6f,0x0,0x77,0x0,0x4d,0x0,0x6f,0x0,0x72,0x0,0x65,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
	// Titlebar_Close_Hover.svg
  0x0,0x18,
  0xe,0x56,0x59,0x47,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x43,0x0,0x6c,0x0,0x6f,0x0,0x73,0x0,0x65,0x0,0x5f,0x0,0x48,0x0,0x6f,
  0x0,0x76,0x0,0x65,0x0,0x72,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// Titlebar_Close.svg
  0x0,0x12,
  0x1,0x46,0xe4,0x67,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x43,0x0,0x6c,0x0,0x6f,0x0,0x73,0x0,0x65,0x0,0x2e,0x0,0x73,0x0,0x76,
  0x0,0x67,
	// Titlebar_Min_Hover.svg
  0x0,0x16,
  0x1,0x8a,0x81,0x87,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x4d,0x0,0x69,0x0,0x6e,0x0,0x5f,0x0,0x48,0x0,0x6f,0x0,0x76,0x0,0x65,
  0x0,0x72,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// Titlebar_Normal_Hover.svg
  0x0,0x19,
  0x0,0x19,0x27,0xa7,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x4e,0x0,0x6f,0x0,0x72,0x0,0x6d,0x0,0x61,0x0,0x6c,0x0,0x5f,0x0,0x48,
  0x0,0x6f,0x0,0x76,0x0,0x65,0x0,0x72,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// Titlebar_Unshade.png
  0x0,0x14,
  0x5,0xc6,0x2b,0x27,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x55,0x0,0x6e,0x0,0x73,0x0,0x68,0x0,0x61,0x0,0x64,0x0,0x65,0x0,0x2e,
  0x0,0x70,0x0,0x6e,0x0,0x67,
	// define-color.svg
  0x0,0x10,
  0x3,0xff,0xbe,0xc7,
  0x0,0x64,
  0x0,0x65,0x0,0x66,0x0,0x69,0x0,0x6e,0x0,0x65,0x0,0x2d,0x0,0x63,0x0,0x6f,0x0,0x6c,0x0,0x6f,0x0,0x72,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// Titlebar_Max_Hover.svg
  0x0,0x16,
  0x1,0x1e,0x81,0x87,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x4d,0x0,0x61,0x0,0x78,0x0,0x5f,0x0,0x48,0x0,0x6f,0x0,0x76,0x0,0x65,
  0x0,0x72,0x0,0x2e,0x0,0x73,0x0,0x76,0x0,0x67,
	// Titlebar_Shade.png
  0x0,0x12,
  0x0,0x76,0xe,0x47,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x53,0x0,0x68,0x0,0x61,0x0,0x64,0x0,0x65,0x0,0x2e,0x0,0x70,0x0,0x6e,
  0x0,0x67,
	// Titlebar_Normal.svg
  0x0,0x13,
  0xe,0x53,0x5b,0x7,
  0x0,0x54,
  0x0,0x69,0x0,0x74,0x0,0x6c,0x0,0x65,0x0,0x62,0x0,0x61,0x0,0x72,0x0,0x5f,0x0,0x4e,0x0,0x6f,0x0,0x72,0x0,0x6d,0x0,0x61,0x0,0x6c,0x0,0x2e,0x0,0x73,
  0x0,0x76,0x0,0x67,
	// ArrowDown.png
  0x0,0xd,
  0x8,0x13,0x81,0x7,
  0x0,0x41,
  0x0,0x72,0x0,0x72,0x0,0x6f,0x0,0x77,0x0,0x44,0x0,0x6f,0x0,0x77,0x0,0x6e,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
	// ribbonPanelOptionButton.png
  0x0,0x1b,
  0x5,0x57,0xcd,0x27,
  0x0,0x72,
  0x0,0x69,0x0,0x62,0x0,0x62,0x0,0x6f,0x0,0x6e,0x0,0x50,0x0,0x61,0x0,0x6e,0x0,0x65,0x0,0x6c,0x0,0x4f,0x0,0x70,0x0,0x74,0x0,0x69,0x0,0x6f,0x0,0x6e,
  0x0,0x42,0x0,0x75,0x0,0x74,0x0,0x74,0x0,0x6f,0x0,0x6e,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
	// theme-office2013.qss
  0x0,0x14,
  0x1,0xd7,0x54,0x3,
  0x0,0x74,
  0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,0x0,0x2d,0x0,0x6f,0x0,0x66,0x0,0x66,0x0,0x69,0x0,0x63,0x0,0x65,0x0,0x32,0x0,0x30,0x0,0x31,0x0,0x33,0x0,0x2e,
  0x0,0x71,0x0,0x73,0x0,0x73,
	// theme-dark2.qss
  0x0,0xf,
  0x0,0x94,0xc6,0x3,
  0x0,0x74,
  0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,0x0,0x2d,0x0,0x64,0x0,0x61,0x0,0x72,0x0,0x6b,0x0,0x32,0x0,0x2e,0x0,0x71,0x0,0x73,0x0,0x73,
	// theme-win7.qss
  0x0,0xe,
  0x5,0xa1,0x4f,0xa3,
  0x0,0x74,
  0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,0x0,0x2d,0x0,0x77,0x0,0x69,0x0,0x6e,0x0,0x37,0x0,0x2e,0x0,0x71,0x0,0x73,0x0,0x73,
	// theme-dark.qss
  0x0,0xe,
  0x5,0xd,0x41,0x43,
  0x0,0x74,
  0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,0x0,0x2d,0x0,0x64,0x0,0x61,0x0,0x72,0x0,0x6b,0x0,0x2e,0x0,0x71,0x0,0x73,0x0,0x73,
	// theme-office2016-blue.qss
  0x0,0x19,
  0x9,0x8f,0x5e,0x3,
  0x0,0x74,
  0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,0x0,0x2d,0x0,0x6f,0x0,0x66,0x0,0x66,0x0,0x69,0x0,0x63,0x0,0x65,0x0,0x32,0x0,0x30,0x0,0x31,0x0,0x36,0x0,0x2d,
  0x0,0x62,0x0,0x6c,0x0,0x75,0x0,0x65,0x0,0x2e,0x0,0x71,0x0,0x73,0x0,0x73,
	// theme-office2021-blue.qss
  0x0,0x19,
  0x9,0x90,0xfe,0x3,
  0x0,0x74,
  0x0,0x68,0x0,0x65,0x0,0x6d,0x0,0x65,0x0,0x2d,0x0,0x6f,0x0,0x66,0x0,0x66,0x0,0x69,0x0,0x63,0x0,0x65,0x0,0x32,0x0,0x30,0x0,0x32,0x0,0x31,0x0,0x2d,
  0x0,0x62,0x0,0x6c,0x0,0x75,0x0,0x65,0x0,0x2e,0x0,0x71,0x0,0x73,0x0,0x73,

};

static const unsigned char qt_resource_struct[] = {
  // :
  0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  // :/SARibbon
  0x0,0x0,0x0,0x20,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  // :/SARibbonTheme
  0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  // :/SARibbonTheme/resource
  0x0,0x0,0x0,0x46,0x0,0x2,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x4,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  // :/SARibbonTheme/resource/theme-dark2.qss
  0x0,0x0,0x3,0x14,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x28,0x75,
0x0,0x0,0x1,0x99,0x84,0xa5,0x49,0x2b,
  // :/SARibbonTheme/resource/theme-office2013.qss
  0x0,0x0,0x2,0xe6,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x21,0x79,
0x0,0x0,0x1,0x99,0x84,0xa5,0x49,0x2c,
  // :/SARibbonTheme/resource/theme-dark.qss
  0x0,0x0,0x3,0x5a,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x39,0x85,
0x0,0x0,0x1,0x99,0x84,0xa5,0x49,0x2b,
  // :/SARibbonTheme/resource/theme-win7.qss
  0x0,0x0,0x3,0x38,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x30,0x82,
0x0,0x0,0x1,0x99,0x84,0xa5,0x49,0x2d,
  // :/SARibbonTheme/resource/theme-office2016-blue.qss
  0x0,0x0,0x3,0x7c,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x40,0xdb,
0x0,0x0,0x1,0x99,0x84,0xa5,0x49,0x2c,
  // :/SARibbonTheme/resource/theme-office2021-blue.qss
  0x0,0x0,0x3,0xb4,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x4a,0x34,
0x0,0x0,0x1,0x99,0x84,0xa5,0x49,0x2d,
  // :/SARibbon/image
  0x0,0x0,0x0,0x36,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xb,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  // :/SARibbon/image/resource
  0x0,0x0,0x0,0x46,0x0,0x2,0x0,0x0,0x0,0xf,0x0,0x0,0x0,0xc,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  // :/SARibbon/image/resource/Titlebar_Normal_Hover.svg
  0x0,0x0,0x1,0x76,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xc,0x6d,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x38,
  // :/SARibbon/image/resource/Titlebar_Shade.png
  0x0,0x0,0x2,0x34,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1a,0xd9,
0x0,0x0,0x1,0x80,0x92,0x3f,0xd9,0x87,
  // :/SARibbon/image/resource/Titlebar_Max_Hover.svg
  0x0,0x0,0x2,0x2,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x18,0xf3,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x34,
  // :/SARibbon/image/resource/Titlebar_Close.svg
  0x0,0x0,0x1,0x1a,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x8,0x33,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x31,
  // :/SARibbon/image/resource/Titlebar_Min_Hover.svg
  0x0,0x0,0x1,0x44,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xa,0x92,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x36,
  // :/SARibbon/image/resource/define-color.svg
  0x0,0x0,0x1,0xdc,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x10,0xbc,
0x0,0x0,0x1,0x88,0x9f,0x5b,0x2,0x1a,
  // :/SARibbon/image/resource/ribbonPanelOptionButton.png
  0x0,0x0,0x2,0xaa,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x20,0x2a,
0x0,0x0,0x1,0x99,0x4a,0xcd,0xe8,0xff,
  // :/SARibbon/image/resource/Titlebar_Unshade.png
  0x0,0x0,0x1,0xae,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xf,0x33,
0x0,0x0,0x1,0x80,0x92,0x3f,0xd9,0x88,
  // :/SARibbon/image/resource/Titlebar_Min.svg
  0x0,0x0,0x0,0x9e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x2,0xe5,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x35,
  // :/SARibbon/image/resource/Titlebar_Max.svg
  0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xff,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x33,
  // :/SARibbon/image/resource/ArrowMore.png
  0x0,0x0,0x0,0xc4,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x4,0xc0,
0x0,0x0,0x1,0x80,0x92,0x3f,0xd9,0x7e,
  // :/SARibbon/image/resource/ArrowDown.png
  0x0,0x0,0x2,0x8a,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1f,0x2a,
0x0,0x0,0x1,0x80,0x92,0x3f,0xd9,0x7d,
  // :/SARibbon/image/resource/ArrowUp.png
  0x0,0x0,0x0,0x5c,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
0x0,0x0,0x1,0x80,0x92,0x3f,0xd9,0x7f,
  // :/SARibbon/image/resource/Titlebar_Normal.svg
  0x0,0x0,0x2,0x5e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1c,0x64,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x37,
  // :/SARibbon/image/resource/Titlebar_Close_Hover.svg
  0x0,0x0,0x0,0xe4,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x5,0xc5,
0x0,0x0,0x1,0x8c,0xa0,0x13,0xb3,0x32,

};
/*** End of inlined file: qrc_SARibbonResource_Datas.cpp ***/


#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)

/*** Start of inlined file: qrc_SARibbonResource_version3.cpp ***/
#ifdef QT_NAMESPACE
#  define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
#  define QT_RCC_MANGLE_NAMESPACE0(x) x
#  define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
#  define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
#  define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
		QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
#else
#   define QT_RCC_PREPEND_NAMESPACE(name) name
#   define QT_RCC_MANGLE_NAMESPACE(name) name
#endif

#ifdef QT_NAMESPACE
namespace QT_NAMESPACE {
#endif

bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);

#if defined(__ELF__) || defined(__APPLE__)
static inline unsigned char qResourceFeatureZlib()
{
	extern const unsigned char qt_resourceFeatureZlib;
	return qt_resourceFeatureZlib;
}
#else
unsigned char qResourceFeatureZlib();
#endif

#ifdef QT_NAMESPACE
}
#endif

int QT_RCC_MANGLE_NAMESPACE(qInitResources_SARibbonResource)();
int QT_RCC_MANGLE_NAMESPACE(qInitResources_SARibbonResource)()
{
	int version = 3;
	QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
		(version, qt_resource_struct, qt_resource_name, qt_resource_data);
	return 1;
}

int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_SARibbonResource)();
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_SARibbonResource)()
{
	int version = 3;
	version += QT_RCC_PREPEND_NAMESPACE(qResourceFeatureZlib());
	QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
	   (version, qt_resource_struct, qt_resource_name, qt_resource_data);
	return 1;
}

namespace {
   struct initializer {
	   initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_SARibbonResource)(); }
	   ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_SARibbonResource)(); }
   } dummy;
}

/*** End of inlined file: qrc_SARibbonResource_version3.cpp ***/


#else

/*** Start of inlined file: qrc_SARibbonResource_version2.cpp ***/
#ifdef QT_NAMESPACE
#  define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
#  define QT_RCC_MANGLE_NAMESPACE0(x) x
#  define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
#  define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
#  define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
		QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
#else
#   define QT_RCC_PREPEND_NAMESPACE(name) name
#   define QT_RCC_MANGLE_NAMESPACE(name) name
#endif

#ifdef QT_NAMESPACE
namespace QT_NAMESPACE {
#endif

bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);

bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);

#ifdef QT_NAMESPACE
}
#endif

int QT_RCC_MANGLE_NAMESPACE(qInitResources_SARibbonResource)();
int QT_RCC_MANGLE_NAMESPACE(qInitResources_SARibbonResource)()
{
	QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
		(0x2, qt_resource_struct, qt_resource_name, qt_resource_data);
	return 1;
}

int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_SARibbonResource)();
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_SARibbonResource)()
{
	QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
	   (0x2, qt_resource_struct, qt_resource_name, qt_resource_data);
	return 1;
}

namespace {
   struct initializer {
	   initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_SARibbonResource)(); }
	   ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_SARibbonResource)(); }
   } dummy;
}

/*** End of inlined file: qrc_SARibbonResource_version2.cpp ***/


#endif
// clang-format on

/*** Start of inlined file: SAColorMenu.cpp ***/
#include <QWidgetAction>
#include <QColorDialog>
#include <QDebug>
#include <QLabel>
#include <QGridLayout>
#include <QPainter>

class SAColorMenu::PrivateData
{
    SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorMenu)
public:
    PrivateData(SAColorMenu* p);
    QColor getColorByDialog();
    void recordCustomColor(const QColor& c);
    QWidgetAction* addWidget(QWidget* w);
    // 创建一个无颜色的icon
    QIcon createNoneColorIcon(QSize baseSize = QSize(32, 32));

public:
    QLabel* mTitleLabel { nullptr };  ///< 主题颜色标题
    QWidgetAction* mTitleLabelAction { nullptr };
    SAColorPaletteGridWidget* mThemeColorsWidget { nullptr };  ///< 主题颜色
    QWidgetAction* mThemeColorPaletteAction { nullptr };       ///< ThemeColorsPalette对应的action
    QAction* mCustomColorAction { nullptr };                   ///< 自定义颜色action
    SAColorGridWidget* mCustomColorsWidget { nullptr };        ///< 自定义颜色记录
    QWidgetAction* mCustomColorsWidgetAction { nullptr };      ///< 自定义颜色窗口对应的action
    QAction* mNoneColorAction { nullptr };                     ///< 无颜色action
    QList< QColor > mCustomColors;
    int mMaxCustomColorSize { 10 };                        ///< 记录最多的自定义颜色数量
    QScopedPointer< QColorDialog > mColorDlg { nullptr };  ///< 颜色对话框
};

SAColorMenu::PrivateData::PrivateData(SAColorMenu* p) : q_ptr(p)
{
}

QColor SAColorMenu::PrivateData::getColorByDialog()
{
    if (nullptr == mColorDlg) {
        mColorDlg.reset(new QColorDialog());
    }
    if (QDialog::Accepted == mColorDlg->exec()) {
        return mColorDlg->currentColor();
    }
    return QColor();
}

void SAColorMenu::PrivateData::recordCustomColor(const QColor& c)
{
    if (mCustomColors.size() < mMaxCustomColorSize) {
        mCustomColors.push_back(c);
    } else {
        // 超过数量，就左移动
        for (int i = 1; i < mCustomColors.size(); ++i) {
            mCustomColors[ i - 1 ] = mCustomColors[ i ];
        }
        mCustomColors.back() = c;
    }
}

QWidgetAction* SAColorMenu::PrivateData::addWidget(QWidget* w)
{
    QWidgetAction* wa = new QWidgetAction(q_ptr);
    wa->setDefaultWidget(w);
    q_ptr->addAction(wa);
    return wa;
}

QIcon SAColorMenu::PrivateData::createNoneColorIcon(QSize baseSize)
{
    QPixmap pixmap(baseSize);
    pixmap.fill(Qt::transparent);
    QPainter p(&pixmap);
    SAColorToolButton::paintNoneColor(&p, QRect(0, 0, pixmap.width(), pixmap.height()).adjusted(1, 1, -1, -1));
    return QIcon(pixmap);
}
//===================================================
// SAColorMenu
//===================================================

SAColorMenu::SAColorMenu(QWidget* parent) : QMenu(parent), d_ptr(new SAColorMenu::PrivateData(this))
{
    init(SA::getStandardColorList());
}

SAColorMenu::SAColorMenu(const QString& title, QWidget* parent)
    : QMenu(title, parent), d_ptr(new SAColorMenu::PrivateData(this))
{
    init(SA::getStandardColorList());
}

SAColorMenu::~SAColorMenu()
{
}

/**
 * @brief 快速绑定ColorToolButton
 * @param btn
 */
void SAColorMenu::bindToColorToolButton(SAColorToolButton* btn)
{
    if (!btn) {
        return;
    }
    if (btn->menu() != this) {
        btn->setMenu(this);
    }
    connect(this, &SAColorMenu::selectedColor, btn, &SAColorToolButton::setColor);
}

/**
 * @brief ThemeColorsPalette对应的action
 * @return
 */
QWidgetAction* SAColorMenu::themeColorsPaletteAction() const
{
    return d_ptr->mThemeColorPaletteAction;
}

/**
 * @brief CustomColorsWidget对应的action
 * @return
 */
QWidgetAction* SAColorMenu::getCustomColorsWidgetAction() const
{
    return d_ptr->mCustomColorsWidgetAction;
}

/**
 * @brief 自定义颜色action
 * @return
 */
QAction* SAColorMenu::customColorAction() const
{
    return d_ptr->mCustomColorAction;
}

/**
 * @brief 获取ThemeColorsPalette
 * @return
 */
SAColorPaletteGridWidget* SAColorMenu::colorPaletteGridWidget() const
{
    return d_ptr->mThemeColorsWidget;
}

/**
 * @brief 获取自定义颜色grid
 * @return
 */
SAColorGridWidget* SAColorMenu::customColorsWidget() const
{
    return d_ptr->mCustomColorsWidget;
}

/**
 * @brief 建立没有颜色的action，可以选择无颜色
 *
 * 无颜色选中会发射selectedColor(QColor())
 * @param on
 */
void SAColorMenu::enableNoneColorAction(bool on)
{
    // 无颜色默认是在自定义颜色的上方
    if (on) {
        if (d_ptr->mNoneColorAction) {
            if (actions().contains(d_ptr->mNoneColorAction)) {
                // 已经包含了NoneColorAction,退出
                return;
            } else {
                insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction);
            }
        } else {
            QIcon ic                = d_ptr->createNoneColorIcon();
            d_ptr->mNoneColorAction = new QAction(ic, tr("None"), this);
            connect(d_ptr->mNoneColorAction, &QAction::triggered, this, &SAColorMenu::onNoneColorActionTriggered);
            insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction);
        }
    } else {
        removeAction(d_ptr->mNoneColorAction);
        // remove后暂时不删除action
    }
}

/**
 * @brief 获取None Color Action
 *
 * @note 注意，enableNoneColorAction(true),之后才不是nullptr
 * @return 如果没有建立NoneColorAction，会返回nullptr
 */
QAction* SAColorMenu::noneColorAction() const
{
    return d_ptr->mNoneColorAction;
}

/**
 * @brief 这是一个辅助槽函数，为了让用户自定义的其他action也能关联menu，可以调用此槽函数，实现selectedColor信号以及menu的隐藏
 * @param c
 */
void SAColorMenu::emitSelectedColor(const QColor& c)
{
    Q_EMIT selectedColor(c);
    hide();
}

void SAColorMenu::init(const QList< QColor >& themeCls)
{
    d_ptr->mTitleLabel = new QLabel(this);
    d_ptr->mTitleLabel->setText(tr("Theme Colors"));
    d_ptr->mTitleLabelAction = d_ptr->addWidget(d_ptr->mTitleLabel);

    d_ptr->mThemeColorsWidget = new SAColorPaletteGridWidget(themeCls, this);
    d_ptr->mThemeColorsWidget->setColorCheckable(false);
    d_ptr->mThemeColorPaletteAction = d_ptr->addWidget(d_ptr->mThemeColorsWidget);

    d_ptr->mCustomColorAction = new QAction(tr("Custom Color"), this);  // cn:自定义颜色
    addAction(d_ptr->mCustomColorAction);

    QSize clrSize              = d_ptr->mThemeColorsWidget->colorIconSize();
    d_ptr->mCustomColorsWidget = new SAColorGridWidget(this);
    d_ptr->mCustomColorsWidget->setRowMinimumHeight(0, clrSize.height());
    d_ptr->mCustomColorsWidget->setHorizontalSpacerToRight();
    d_ptr->mCustomColorsWidget->setColorIconSize(clrSize);
    d_ptr->mCustomColorsWidget->setColumnCount(d_ptr->mMaxCustomColorSize);
    d_ptr->mCustomColorsWidget->setColorCheckable(false);
    d_ptr->mCustomColorsWidgetAction = d_ptr->addWidget(d_ptr->mCustomColorsWidget);

    connect(d_ptr->mCustomColorAction, &QAction::triggered, this, &SAColorMenu::onCustomColorActionTriggered);
    connect(d_ptr->mThemeColorsWidget, &SAColorPaletteGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor);
    connect(d_ptr->mCustomColorsWidget, &SAColorGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor);
}

/**
 * @brief 自定义颜色
 * @param on
 */
void SAColorMenu::onCustomColorActionTriggered(bool on)
{
    Q_UNUSED(on);
    QColor c = d_ptr->getColorByDialog();
    if (c.isValid()) {
        d_ptr->recordCustomColor(c);
        d_ptr->mCustomColorsWidget->setColorList(d_ptr->mCustomColors);
        updateGeometry();
        emitSelectedColor(c);
    }
}

/**
 * @brief 无颜色
 * @param on
 */
void SAColorMenu::onNoneColorActionTriggered(bool on)
{
    Q_UNUSED(on);
    emitSelectedColor(QColor());
}

/*** End of inlined file: SAColorMenu.cpp ***/

/*** Start of inlined file: SAColorGridWidget.cpp ***/
#include <QGridLayout>
#include <cmath>
#include <QButtonGroup>

class SAColorGridWidget::PrivateData
{
    SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorGridWidget)
public:
    PrivateData(SAColorGridWidget* p);
    // 获取ColorToolButton
    SAColorToolButton* getColorToolButtonAt(int index);
    SAColorToolButton* getColorToolButtonAt(int r, int c);
    SAColorToolButton* getCheckedButton() const;
    void updateGridColor(bool isRemoveSpacer = false);
    void updateGridColorSize();
    void updateGridColorCheckable();
    void iterationColorBtns(SAColorGridWidget::FunColorBtn fn);
    void removeAt(int r, int c);
    void setColorAt(const QColor& clr, int r, int c);
    bool isSpacer(int r, int c) const;

public:
    QList< QColor > mColors;
    QGridLayout* mGridLayout { nullptr };
    QButtonGroup* mButtonGroup { nullptr };
    QSize mIconSize { 16, 16 };
    int mColumnCount { 8 };  ///< 列数，行数量会根据列数量来匹配,如果设置-1或者0，说明不限定列数量，这样会只有一行
    bool mColorCheckable;    ///< 设置颜色是否是checkable
    bool mHorizontalSpacerToRight { false };  ///< 最右边是否有弹簧
};

SAColorGridWidget::PrivateData::PrivateData(SAColorGridWidget* p) : q_ptr(p)
{
    mGridLayout = new QGridLayout(p);
    p->setLayout(mGridLayout);
    mGridLayout->setSpacing(0);
    mGridLayout->setContentsMargins(1, 1, 1, 1);
    mButtonGroup = new QButtonGroup(p);
    mButtonGroup->setExclusive(true);
    p->setMinimumHeight(mIconSize.height());
    p->setMinimumWidth(mIconSize.width());
}

SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int index)
{
    QLayoutItem* item = mGridLayout->itemAt(index);
    if (nullptr == item) {
        return nullptr;
    }
    return qobject_cast< SAColorToolButton* >(item->widget());
}

SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int r, int c)
{
    QLayoutItem* item = mGridLayout->itemAtPosition(r, c);
    if (nullptr == item) {
        return nullptr;
    }
    return qobject_cast< SAColorToolButton* >(item->widget());
}

SAColorToolButton* SAColorGridWidget::PrivateData::getCheckedButton() const
{
    return qobject_cast< SAColorToolButton* >(mButtonGroup->checkedButton());
}

/**
 * @brief 根据mColors更新布局
 */
void SAColorGridWidget::PrivateData::updateGridColor(bool isRemoveSpacer)
{
    int row = 1;
    int col = mColumnCount;
    if (col <= 0) {
        col = mColors.size();
    } else {
        row = std::ceil(mColors.size() / (float)col);
    }
    int index = 0;
    for (int r = 0; r < row; ++r) {
        for (int c = 0; c < col; ++c) {
            if (index < mColors.size()) {
                setColorAt(mColors[ index ], r, c);
                ++index;
            } else {
                removeAt(r, c);
            }
        }
    }
    // 清除多余单元格
    int nowGridRow = mGridLayout->rowCount();
    int nowGridCol = mGridLayout->columnCount();
    if (nowGridRow > row) {
        // 多余的清除
        for (int r = row; r < nowGridRow; ++r) {
            for (int c = 0; c < nowGridCol; ++c) {
                removeAt(r, c);
            }
        }
    }
    if (nowGridCol > col) {
        // 多余的列清除
        for (int r = 0; r < row; ++r) {
            for (int c = col; c < nowGridCol; ++c) {
                if (isRemoveSpacer) {
                    removeAt(r, c);
                } else {
                    if (!isSpacer(r, c)) {
                        removeAt(r, c);
                    }
                }
            }
        }
    }
}

/**
 * @brief 更新colorsize
 */
void SAColorGridWidget::PrivateData::updateGridColorSize()
{
    QSize s = mIconSize;
    iterationColorBtns([ s ](SAColorToolButton* btn) {
        if (btn) {
            btn->setIconSize(s);
        }
    });
}

void SAColorGridWidget::PrivateData::updateGridColorCheckable()
{
    bool v = mColorCheckable;
    iterationColorBtns([ v ](SAColorToolButton* btn) {
        if (btn) {
            btn->setCheckable(v);
        }
    });
}

/**
 * @brief 遍历所有的button
 * @param fn
 */
void SAColorGridWidget::PrivateData::iterationColorBtns(FunColorBtn fn)
{
    int cnt = mGridLayout->count();
    for (int i = 0; i < cnt; ++i) {
        SAColorToolButton* tl = getColorToolButtonAt(i);
        fn(tl);
    }
}

/**
 * @brief 删除网格窗口
 * @param r
 * @param c
 */
void SAColorGridWidget::PrivateData::removeAt(int r, int c)
{
    QLayoutItem* item = mGridLayout->itemAtPosition(r, c);
    if (item) {
        QWidget* w = item->widget();
        mGridLayout->removeItem(item);
        delete item;
        if (w) {
            w->deleteLater();
        }
    }
}

void SAColorGridWidget::PrivateData::setColorAt(const QColor& clr, int r, int c)
{
    QLayoutItem* item = mGridLayout->itemAtPosition(r, c);
    if (item) {
        SAColorToolButton* tl = qobject_cast< SAColorToolButton* >(item->widget());
        if (tl) {
            tl->setColor(clr);
        }
    } else {
        SAColorToolButton* tl = new SAColorToolButton(SAColorToolButton::NoColorMenu, q_ptr);
        tl->setToolButtonStyle(Qt::ToolButtonIconOnly);
        tl->setIconSize(mIconSize);
        tl->setMargins(QMargins(4, 4, 4, 4));
        tl->setColor(clr);
        tl->setCheckable(mColorCheckable);
        tl->setAutoRaise(true);
        mButtonGroup->addButton(tl, r + c);
        mGridLayout->addWidget(tl, r, c);
    }
}

bool SAColorGridWidget::PrivateData::isSpacer(int r, int c) const
{
    QLayoutItem* item = mGridLayout->itemAtPosition(r, c);
    if (item) {
        if (QSpacerItem* si = dynamic_cast< QSpacerItem* >(item)) {
            return true;
        }
    }
    return false;
}

//==============================================================
// SAColorGridWidget
//==============================================================

SAColorGridWidget::SAColorGridWidget(QWidget* par) : QWidget(par), d_ptr(new SAColorGridWidget::PrivateData(this))
{
    connect(d_ptr->mButtonGroup,
            QOverload< QAbstractButton* >::of(&QButtonGroup::buttonClicked),
            this,
            &SAColorGridWidget::onButtonClicked);
    connect(d_ptr->mButtonGroup,
            QOverload< QAbstractButton* >::of(&QButtonGroup::buttonPressed),
            this,
            &SAColorGridWidget::onButtonPressed);
    connect(d_ptr->mButtonGroup,
            QOverload< QAbstractButton* >::of(&QButtonGroup::buttonReleased),
            this,
            &SAColorGridWidget::onButtonReleased);
    connect(d_ptr->mButtonGroup,
            QOverload< QAbstractButton*, bool >::of(&QButtonGroup::buttonToggled),
            this,
            &SAColorGridWidget::onButtonToggled);
}

SAColorGridWidget::~SAColorGridWidget()
{
}

/**
 * @brief 设置列数，行数量会根据列数量来匹配,如果设置-1或者0，说明不限定列数量，这样会只有一行
 * @param c
 */
void SAColorGridWidget::setColumnCount(int c)
{
    d_ptr->mColumnCount = c;
    d_ptr->updateGridColor(true);
    if (d_ptr->mHorizontalSpacerToRight) {
        setHorizontalSpacerToRight();
    }
    updateGeometry();
}

/**
 * @brief 设置颜色列表
 * @param c
 */
void SAColorGridWidget::setColorList(const QList< QColor >& cls)
{
    d_ptr->mColors = cls;
    d_ptr->updateGridColor();
    updateGeometry();
}

/**
 * @brief 获取颜色列表
 * @return
 */
QList< QColor > SAColorGridWidget::getColorList() const
{
    return d_ptr->mColors;
}

/**
 * @brief 获取间隔
 * @return
 */
int SAColorGridWidget::spacing() const
{
    return d_ptr->mGridLayout->spacing();
}

/**
 * @brief 设置间隔
 * @param v
 */
void SAColorGridWidget::setSpacing(int v)
{
    d_ptr->mGridLayout->setSpacing(v);
}

/**
 * @brief 获取颜色的数量
 * @return
 */
int SAColorGridWidget::colorCount() const
{
    return d_ptr->mColors.size();
}

/**
 * @brief 设置图标 size
 * @return
 */
void SAColorGridWidget::setColorIconSize(const QSize& s)
{
    d_ptr->mIconSize = s;
    setMinimumHeight(s.height());
    setMinimumWidth(s.width());
    d_ptr->updateGridColorSize();
}

/**
 * @brief 获取图标 size
 * @return
 */
QSize SAColorGridWidget::colorIconSize() const
{
    return d_ptr->mIconSize;
}

/**
 * @brief 设置颜色是否是checkable
 *
 * checkable的颜色按钮是可checked的
 * @param on
 */
void SAColorGridWidget::setColorCheckable(bool on)
{
    d_ptr->mColorCheckable = on;
    d_ptr->updateGridColorCheckable();
}

/**
 * @brief 颜色是否是checkable
 * @return
 */
bool SAColorGridWidget::isColorCheckable() const
{
    return d_ptr->mColorCheckable;
}

/**
 * @brief 获取当前选中的颜色
 * @return
 */
QColor SAColorGridWidget::currentCheckedColor() const
{
    QAbstractButton* btn = d_ptr->mButtonGroup->checkedButton();
    if (nullptr == btn) {
        return QColor();
    }
    SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn);
    if (nullptr == t) {
        return QColor();
    }
    return t->color();
}

/**
 * @brief 获取index对应的colorbutton
 * @param index
 * @return 如果没有返回nullptr
 */
SAColorToolButton* SAColorGridWidget::colorButton(int index) const
{
    return d_ptr->getColorToolButtonAt(index);
}

/**
 * @brief 等同GridLayout的VerticalSpacing属性
 * @param v
 */
void SAColorGridWidget::setVerticalSpacing(int v)
{
    d_ptr->mGridLayout->setVerticalSpacing(v);
}
/**
 * @brief 等同GridLayout的VerticalSpacing属性
 * @return
 */
int SAColorGridWidget::verticalSpacing() const
{
    return d_ptr->mGridLayout->verticalSpacing();
}
/**
 * @brief 等同GridLayout的HorizontalSpacing属性
 * @param v
 */
void SAColorGridWidget::setHorizontalSpacing(int v)
{
    d_ptr->mGridLayout->setHorizontalSpacing(v);
}
/**
 * @brief 等同GridLayout的HorizontalSpacing属性
 * @return
 */
int SAColorGridWidget::horizontalSpacing() const
{
    return d_ptr->mGridLayout->horizontalSpacing();
}

/**
 * @brief 清除选中状态，这时没有颜色是选中的
 */
void SAColorGridWidget::clearCheckedState()
{
    if (d_ptr->mButtonGroup->exclusive()) {
        SAColorToolButton* btn = d_ptr->getCheckedButton();
        if (btn) {
            d_ptr->mButtonGroup->setExclusive(false);
            btn->setChecked(false);
            d_ptr->mButtonGroup->setExclusive(true);
        }
    } else {
        d_ptr->iterationColorBtns([](SAColorToolButton* btn) {
            if (btn->isChecked()) {
                btn->setChecked(false);
            }
        });
    }
}

void SAColorGridWidget::iterationColorBtns(SAColorGridWidget::FunColorBtn fn)
{
    d_ptr->iterationColorBtns(fn);
}

void SAColorGridWidget::setRowMinimumHeight(int row, int minSize)
{
    d_ptr->mGridLayout->setRowMinimumHeight(row, minSize);
}

void SAColorGridWidget::setHorizontalSpacerToRight(bool on)
{
    if (on) {
        QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
        d_ptr->mGridLayout->addItem(horizontalSpacer, 0, d_ptr->mColumnCount, 1, 1);
    } else {
        d_ptr->removeAt(0, d_ptr->mColumnCount);
    }
    d_ptr->mHorizontalSpacerToRight = on;
}

void SAColorGridWidget::onButtonClicked(QAbstractButton* btn)
{
    SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn);
    if (t) {
        Q_EMIT colorClicked(t->color());
    }
}

void SAColorGridWidget::onButtonPressed(QAbstractButton* btn)
{
    SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn);
    if (t) {
        Q_EMIT colorPressed(t->color());
    }
}

void SAColorGridWidget::onButtonToggled(QAbstractButton* btn, bool on)
{
    SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn);
    if (t) {
        Q_EMIT colorToggled(t->color(), on);
    }
}

QSize SAColorGridWidget::sizeHint() const
{
    return d_ptr->mGridLayout->sizeHint();
    //    int w = d_ptr->mIconSize.width() + d_ptr->mGridLayout->verticalSpacing();
    //    int h = d_ptr->mIconSize.height();
    //    if (d_ptr->mColumnCount > 0) {
    //        h *= d_ptr->mColumnCount;
    //        if (!d_ptr->mColors.empty()) {
    //            int r = std::ceil(d_ptr->mColors.size() / (float)(d_ptr->mColumnCount));
    //            if (r > 0) {
    //                w *= r;
    //            }
    //        }
    //    }
    //    return QSize(w, h);
}

void SAColorGridWidget::onButtonReleased(QAbstractButton* btn)
{
    SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn);
    if (t) {
        Q_EMIT colorReleased(t->color());
    }
}

namespace SA
{

QList< QColor > getStandardColorList()
{
    static QList< QColor > s_standardColorList({ QColor(192, 0, 0),
                                                 QColor(255, 0, 0),
                                                 QColor(255, 192, 0),
                                                 QColor(255, 255, 0),
                                                 QColor(146, 208, 80),
                                                 QColor(0, 176, 80),
                                                 QColor(0, 176, 240),
                                                 QColor(0, 112, 192),
                                                 QColor(0, 32, 96),
                                                 QColor(112, 48, 160) });
    return s_standardColorList;
}
}

/*** End of inlined file: SAColorGridWidget.cpp ***/

/*** Start of inlined file: SAColorPaletteGridWidget.cpp ***/
// Qt
#include <QMenu>
#include <QWidgetAction>
#include <QVBoxLayout>
#include <QColorDialog>
// SA

class SAColorPaletteGridWidget::PrivateData
{
    SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorPaletteGridWidget)
public:
    PrivateData(SAColorPaletteGridWidget* p);
    // 生成color palette
    QList< QColor > makeColorPalette(const QList< QColor >& clrList) const;

public:
    QList< int > mFactor { 180, 160, 140, 75, 50 };  ///< palette的比例因子，将调用QColor的lighter函数执行
    QVBoxLayout* mLayout { nullptr };                ///< 垂直布局
    SAColorGridWidget* mMainColorList { nullptr };   ///< 这个用于显示标准颜色
    SAColorGridWidget* mPaletteColorGrid { nullptr };  ///< 这个用于生成3行亮色，2行暗色的palette
};

SAColorPaletteGridWidget::PrivateData::PrivateData(SAColorPaletteGridWidget* p) : q_ptr(p)
{
    mLayout = new QVBoxLayout(p);
    p->setLayout(mLayout);
    mMainColorList    = new SAColorGridWidget(p);
    mPaletteColorGrid = new SAColorGridWidget(p);
    mLayout->addWidget(mMainColorList);
    mLayout->addWidget(mPaletteColorGrid);
    mLayout->setContentsMargins(1, 1, 1, 1);
    mLayout->setSpacing(8);
    QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
    mMainColorList->setSizePolicy(sizePolicy);
    mPaletteColorGrid->setSizePolicy(sizePolicy);
    mMainColorList->setColumnCount(0);
    mPaletteColorGrid->setVerticalSpacing(0);
}

QList< QColor > SAColorPaletteGridWidget::PrivateData::makeColorPalette(const QList< QColor >& clrList) const
{
    QList< QColor > res;
    for (int f : qAsConst(mFactor)) {
        for (const QColor& c : qAsConst(clrList)) {
            res.append(c.lighter(f));
        }
    }
    return res;
}
//==============================================================
// name
//==============================================================

/**
 * @brief 使用默认的标准颜色生成一个color palette
 * @param par
 */
SAColorPaletteGridWidget::SAColorPaletteGridWidget(QWidget* par) : QWidget(par), d_ptr(new PrivateData(this))
{
    init();
    setColorList(SA::getStandardColorList());
}

/**
 * @brief 根据指定的颜色生成一个color palette
 * @param cls
 * @param par
 */
SAColorPaletteGridWidget::SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par)
    : QWidget(par), d_ptr(new PrivateData(this))
{
    init();
    setColorList(cls);
}

SAColorPaletteGridWidget::~SAColorPaletteGridWidget()
{
}
void SAColorPaletteGridWidget::init()
{
    connect(d_ptr->mMainColorList, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onMainColorClicked);
    connect(d_ptr->mPaletteColorGrid, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onPaletteColorClicked);
    QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
    setSizePolicy(sizePolicy);
    setColorIconSize(QSize(10, 10));
}

/**
 * @brief 设置颜色系列，颜色系列的个数决定了主色卡的数量，palette色卡会根据factor进行生成
 * @param cls
 */
void SAColorPaletteGridWidget::setColorList(const QList< QColor >& cls)
{
    d_ptr->mMainColorList->setColumnCount(0);
    d_ptr->mMainColorList->setColorList(cls);
    d_ptr->mPaletteColorGrid->setColumnCount(cls.size());
    d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(cls));
}

/**
 * @brief 获取颜色系列
 * @return
 */
QList< QColor > SAColorPaletteGridWidget::colorList() const
{
    return d_ptr->mMainColorList->getColorList();
}

/**
 * @brief 设置颜色深浅比例factor，factor决定了palette的行数，factor有5个，就有5行
 *
 * 默认为{ 180, 160, 140, 75, 50 },相当于会有5行，每个系数会对标准颜色执行QColor::lighter操作
 * @param factor
 */
void SAColorPaletteGridWidget::setFactor(const QList< int >& factor)
{
    d_ptr->mFactor = factor;
    d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(d_ptr->mMainColorList->getColorList()));
}

/**
 * @brief 获取色卡
 * @return
 */
QList< int > SAColorPaletteGridWidget::factor() const
{
    return d_ptr->mFactor;
}

/**
 * @brief 设置颜色块的尺寸，默认为10*10
 * @param s
 */
void SAColorPaletteGridWidget::setColorIconSize(const QSize& s)
{
    d_ptr->mMainColorList->setColorIconSize(s);
    d_ptr->mPaletteColorGrid->setColorIconSize(s);
}

QSize SAColorPaletteGridWidget::colorIconSize() const
{
    return d_ptr->mMainColorList->colorIconSize();
}

/**
 * @brief 设置颜色块是否能被checked
 * @param on
 */
void SAColorPaletteGridWidget::setColorCheckable(bool on)
{
    d_ptr->mMainColorList->setColorCheckable(on);
    d_ptr->mPaletteColorGrid->setColorCheckable(on);
}

bool SAColorPaletteGridWidget::isColorCheckable() const
{
    return d_ptr->mMainColorList->isColorCheckable();
}

void SAColorPaletteGridWidget::onMainColorClicked(const QColor& c)
{
    d_ptr->mPaletteColorGrid->clearCheckedState();
    Q_EMIT colorClicked(c);
}

void SAColorPaletteGridWidget::onPaletteColorClicked(const QColor& c)
{
    d_ptr->mMainColorList->clearCheckedState();
    Q_EMIT colorClicked(c);
}

/*** End of inlined file: SAColorPaletteGridWidget.cpp ***/

/*** Start of inlined file: SAColorToolButton.cpp ***/
#include <QApplication>
#include <QPaintEvent>
#include <QStylePainter>
#include <QStyleOption>
#include <QStyleOptionToolButton>
#include <QResizeEvent>
#include <QDebug>

class SAColorToolButton::PrivateData
{
    SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorToolButton)
public:
    PrivateData(SAColorToolButton* p);
    void calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect);
    void calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect);
    void calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect);
    void calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect);
    QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect);
    QRect getButtonRect(const QStyleOptionToolButton& opt);
    QRect getIndicatorRect(const QStyleOptionToolButton& opt);
    QStyle::State getButtonStyleState(const QStyleOptionToolButton& opt);
    QStyle::State getButtonMenuStyleState(const QStyleOptionToolButton& opt);
    void removeMenu();

public:
    QColor mColor { Qt::white };
    int mSpacing { 2 };                                        ///< 间隔
    QMargins mMargins { 3, 3, 3, 3 };                          ///< box
    ColorToolButtonStyle mColorButtonStyle { WithColorMenu };  ///< 样式
    static int s_indicatorArrorWidth;                          ///< 菜单宽度
};

int SAColorToolButton::PrivateData::s_indicatorArrorWidth = 8;

SAColorToolButton::PrivateData::PrivateData(SAColorToolButton* p) : q_ptr(p)
{
}

void SAColorToolButton::PrivateData::calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt,
                                                                  QRect& iconRect,
                                                                  QRect& textRect,
                                                                  QRect& colorRect)
{
    // 确定文本区域
    textRect         = QRect();
    QRect buttonRect = getButtonRect(opt);
    if (opt.icon.isNull()) {
        colorRect = buttonRect;
        iconRect  = QRect();
    } else {
        QSize tmpSize = opt.iconSize;
        if (tmpSize.isNull()) {
            tmpSize = QSize(16, 16);
        }
        tmpSize         = buttonRect.size().boundedTo(tmpSize);
        int colorHeight = tmpSize.height() / 4;
        int totalHeight = colorHeight + tmpSize.height() + mSpacing;
        if (totalHeight > buttonRect.height()) {
            // 过高，这时要通过高度反推tmpSize和colorHeight
            colorHeight = (buttonRect.height() - mSpacing) / 5;
            tmpSize.setHeight(colorHeight * 4);
            if (opt.iconSize.height() > 0) {
                tmpSize.setWidth(tmpSize.height() * opt.iconSize.width() / opt.iconSize.height());  // 等比例
            }
            tmpSize = buttonRect.size().boundedTo(tmpSize);
        }
        // 有icon，颜色位于图标下面
        int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2;  //(ButtonHeight-TotalHeight)/2
        int x = (buttonRect.width() - tmpSize.width()) / 2;
        iconRect  = QRect(buttonRect.left() + x, buttonRect.top() + y, tmpSize.width(), tmpSize.height());
        colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight);
    }
}

/**
 * @brief 纯文本情况，左边有5像素显示颜色
 * @param opt
 * @param iconRect
 * @param textRect
 * @param colorRect 左边有5像素显示颜色
 */
void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt,
                                                                  QRect& iconRect,
                                                                  QRect& textRect,
                                                                  QRect& colorRect)
{
    QRect buttonRect = getButtonRect(opt);
    iconRect         = QRect();
    QSize colorSize  = opt.iconSize;
    if (colorSize.isNull()) {
        colorSize = QSize(16, 16);
    }
    colorSize = buttonRect.size().boundedTo(colorSize);
    colorRect = QRect(buttonRect.left(),
                      buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2,
                      colorSize.width(),
                      colorSize.height());
    textRect  = buttonRect.adjusted(colorRect.right() + mSpacing, 0, 0, 0);
}

void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt,
                                                                        QRect& iconRect,
                                                                        QRect& textRect,
                                                                        QRect& colorRect)
{
    QRect buttonRect = getButtonRect(opt);
    if (opt.icon.isNull()) {
        QSize colorSize = opt.iconSize;
        if (colorSize.isNull()) {
            colorSize = QSize(16, 16);
        }
        // 说明没有icon
        // 这时所有都是color
        // iconRect=Null
        colorSize = buttonRect.size().boundedTo(colorSize);
        iconRect  = QRect();
        colorRect = QRect(buttonRect.left(),
                          buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2,
                          colorSize.width(),
                          colorSize.height());
        textRect  = buttonRect.adjusted(colorRect.width() + mSpacing, 0, 0, 0);
    } else {  // 有图标
        QSize tmpSize = opt.iconSize;
        if (tmpSize.isNull()) {
            tmpSize = QSize(16, 16);
        }
        tmpSize         = buttonRect.size().boundedTo(tmpSize);
        int colorHeight = tmpSize.height() / 4;
        int totalHeight = colorHeight + tmpSize.height() + mSpacing;
        if (totalHeight > buttonRect.height()) {
            // 过高，这时要通过高度反推tmpSize和colorHeight
            colorHeight = (buttonRect.height() - mSpacing) / 5;
            tmpSize.setHeight(colorHeight * 4);
            tmpSize.setWidth(tmpSize.height());  // 对于异形也设置为正方行
        }
        // 有icon，颜色位于图标下面
        int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2;  //(ButtonHeight-TotalHeight)/2
        iconRect  = QRect(buttonRect.left(), buttonRect.top() + y, tmpSize.width(), tmpSize.height());
        colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight);
        textRect  = buttonRect.adjusted(iconRect.right() + mSpacing, 0, 0, 0);
    }
}

void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt,
                                                                       QRect& iconRect,
                                                                       QRect& textRect,
                                                                       QRect& colorRect)
{
    QRect buttonRect = getButtonRect(opt);
    QSize tmpSize    = opt.iconSize;
    if (tmpSize.isNull()) {
        tmpSize = QSize(16, 16);
    }
    // 获取字体高度
    int textHeight = opt.fontMetrics.height();
    tmpSize        = buttonRect.size().boundedTo(tmpSize);
    if (opt.icon.isNull()) {
        int totalHeight = textHeight + opt.iconSize.height() + mSpacing;
        if (totalHeight < buttonRect.height()) {
            // 足够高
            colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2,
                              buttonRect.top() + (buttonRect.height() - totalHeight) / 2,
                              tmpSize.width(),
                              opt.iconSize.height());
        } else {
            // 空间不足
            colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2,
                              buttonRect.top() + mSpacing,
                              tmpSize.width(),
                              opt.iconSize.height());
        }
        iconRect = QRect();
        textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight);

    } else {
        // 有图标
        int colorHeight = tmpSize.height() / 4;
        int totalHeight = textHeight + opt.iconSize.height() + colorHeight + 2 * mSpacing;
        if (totalHeight < buttonRect.height()) {
            // 高度空间足够
            // 先布置icon
            iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2,
                             buttonRect.top() + (buttonRect.height() - totalHeight) / 2,
                             tmpSize.width(),
                             opt.iconSize.height());

        } else {
            // 空间不足
            iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2,
                             buttonRect.top() + mSpacing,
                             tmpSize.width(),
                             opt.iconSize.height());
        }
        colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight);
        textRect  = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight);
    }
}

QPixmap SAColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect)
{
    if (opt.icon.isNull()) {
        return (QPixmap());
    }
    // 有图标
    QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off;
    QIcon::Mode mode;
    if (!(opt.state & QStyle::State_Enabled)) {
        mode = QIcon::Disabled;
    } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) {
        mode = QIcon::Active;
    } else {
        mode = QIcon::Normal;
    }
    // return (opt.icon.pixmap(this->window()->windowHandle(), opt.rect.size().boundedTo(realConSize), mode, state));
    return (opt.icon.pixmap(iconRect.size(), mode, state));
}

/**
 * @brief 获取按钮的矩形区域
 * @param opt
 * @return
 */
QRect SAColorToolButton::PrivateData::getButtonRect(const QStyleOptionToolButton& opt)
{
    QRect btnRect = q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, q_ptr);
    return btnRect.marginsRemoved(mMargins);
}

/**
 * @brief 获取ToolButtonMenu的区域
 * @note 注意，如果不是QStyleOptionToolButton::MenuButtonPopup|QStyleOptionToolButton::HasMenu下调用此函数，返回的是QRect()
 * @param opt
 * @return
 */
QRect SAColorToolButton::PrivateData::getIndicatorRect(const QStyleOptionToolButton& opt)
{
    if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) {
        return q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButtonMenu, q_ptr);
    }
    return QRect();
}

/**
 * @brief 基于Qt qcommonStyle源码，对QStyleOptionToolButton的style修正
 * @param opt
 * @return
 */
QStyle::State SAColorToolButton::PrivateData::getButtonStyleState(const QStyleOptionToolButton& opt)
{
    QStyle::State bflags = opt.state & ~QStyle::State_Sunken;
    if (bflags & QStyle::State_AutoRaise) {
        if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) {
            bflags &= ~QStyle::State_Raised;
        }
    }
    if (opt.state & QStyle::State_Sunken) {
        if (opt.activeSubControls & QStyle::SC_ToolButton) {
            bflags |= QStyle::State_Sunken;
        }
    }
    return bflags;
}

/**
 * @brief 基于Qt qcommonStyle源码，对QStyleOptionToolButton的style修正
 * @param opt
 * @return
 */
QStyle::State SAColorToolButton::PrivateData::getButtonMenuStyleState(const QStyleOptionToolButton& opt)
{
    QStyle::State mflags = opt.state & ~QStyle::State_Sunken;
    if (mflags & QStyle::State_AutoRaise) {
        if (!(mflags & QStyle::State_MouseOver) || !(mflags & QStyle::State_Enabled)) {
            mflags &= ~QStyle::State_Raised;
        }
    }
    if (opt.state & QStyle::State_Sunken) {
        mflags |= QStyle::State_Sunken;
    }
    return mflags;
}

void SAColorToolButton::PrivateData::removeMenu()
{
    QMenu* m = q_ptr->menu();
    if (m) {
        q_ptr->setMenu(nullptr);
        m->deleteLater();
    }
}

//==============================================================
// SAColorToolButton
//==============================================================

SAColorToolButton::SAColorToolButton(QWidget* parent) : QToolButton(parent), d_ptr(new PrivateData(this))
{
    setColorToolButtonStyle(WithColorMenu);
    connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked);
}

SAColorToolButton::SAColorToolButton(ColorToolButtonStyle style, QWidget* parent)
    : QToolButton(parent), d_ptr(new PrivateData(this))
{
    setColorToolButtonStyle(style);
    connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked);
}

SAColorToolButton::~SAColorToolButton()
{
}

/**
 * @brief 获取颜色
 * @return
 */
QColor SAColorToolButton::color() const
{
    return d_ptr->mColor;
}

/**
 * @brief 设置Margins
 * @param mg
 */
void SAColorToolButton::setMargins(const QMargins& mg)
{
    d_ptr->mMargins = mg;
    repaint();
}

QMargins SAColorToolButton::margins() const
{
    return d_ptr->mMargins;
}

/**
 * @brief 绘制无颜色表示
 * @param p
 * @param colorRect 绘制的区域
 */
void SAColorToolButton::paintNoneColor(QPainter* p, const QRect& colorRect)
{
    p->save();
    QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap);
    p->setPen(pen);
    p->setRenderHint(QPainter::SmoothPixmapTransform, true);
    p->setRenderHint(QPainter::Antialiasing, true);
    int ss = colorRect.width() / 3;
    p->drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top()));
    pen.setColor(Qt::black);
    p->setPen(pen);
    p->drawRect(colorRect);
    p->restore();
}

/**
 * @brief 设置颜色按钮的样式
 * @param s
 */
void SAColorToolButton::setColorToolButtonStyle(ColorToolButtonStyle s)
{
    if (NoColorMenu == s) {
        d_ptr->removeMenu();
    } else {
        createColorMenu();
    }
    d_ptr->mColorButtonStyle = s;
}

/**
 * @brief 颜色按钮的样式
 * @return
 */
SAColorToolButton::ColorToolButtonStyle SAColorToolButton::colorToolButtonStyle() const
{
    return d_ptr->mColorButtonStyle;
}

/**
 * @brief 获取colorMenu
 *
 * @note 注意，这个函数很有可能会返回nullptr，如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单，此函数返回nullptr
 * @return SAColorMenu指针，如果没有，返回nullptr
 */
SAColorMenu* SAColorToolButton::colorMenu() const
{
    return qobject_cast< SAColorMenu* >(menu());
}

/**
 * @brief 建立标准的颜色菜单
 * @return
 */
SAColorMenu* SAColorToolButton::createColorMenu()
{
    setPopupMode(QToolButton::InstantPopup);
    SAColorMenu* m = new SAColorMenu(this);
    m->enableNoneColorAction(true);
    QAction* customColor = m->customColorAction();
    if (customColor) {
        customColor->setIcon(QIcon(":/SARibbon/image/resource/define-color.svg"));
    }
    connect(m, &SAColorMenu::selectedColor, this, &SAColorToolButton::setColor);
    setMenu(m);
    return m;
}

/**
 * @brief 设置颜色
 * @note 会发射@sa colorChanged 信号
 * @param c
 */
void SAColorToolButton::setColor(const QColor& c)
{
    if (d_ptr->mColor != c) {
        d_ptr->mColor = c;
        repaint();
        Q_EMIT colorChanged(c);
    }
}

/**
 * @brief 计算各个基本组件的位置
 * @param opt
 * @param iconRect 图标区域
 * @param textRect 文本区域
 * @param colorRect 颜色区域
 */
void SAColorToolButton::calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect)
{
    switch (opt.toolButtonStyle) {
    case Qt::ToolButtonTextOnly:
        d_ptr->calcSizeOfToolButtonTextOnly(opt, iconRect, textRect, colorRect);
        break;
    case Qt::ToolButtonTextBesideIcon:
        d_ptr->calcSizeOfToolButtonTextBesideIcon(opt, iconRect, textRect, colorRect);
        break;
    case Qt::ToolButtonTextUnderIcon:
        d_ptr->calcSizeOfToolButtonTextUnderIcon(opt, iconRect, textRect, colorRect);
        break;
    case Qt::ToolButtonFollowStyle:
    case Qt::ToolButtonIconOnly:
        d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect);
        break;
    default:
        d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect);
        break;
    }
}

void SAColorToolButton::paintEvent(QPaintEvent* e)
{
    Q_UNUSED(e);
    QStylePainter p(this);

    QStyleOptionToolButton opt;

    initStyleOption(&opt);
    QRect iconRect, colorRect, textRect;
    calcRect(opt, iconRect, textRect, colorRect);
    paintButton(&p, opt);
    // 绘制图标
    paintIcon(&p, iconRect, opt);
    // 绘制文字
    paintText(&p, textRect, opt);
    // 绘制颜色
    paintColor(&p, colorRect, d_ptr->mColor, opt);
}

void SAColorToolButton::resizeEvent(QResizeEvent* e)
{
    // 在resizeevent计算绘图所需的尺寸，避免在绘图过程中实时绘制提高效率
    QToolButton::resizeEvent(e);
}

/**
 * @brief sizeHint重载
 * @return
 */
QSize SAColorToolButton::sizeHint() const
{
    ensurePolished();
    QStyleOptionToolButton opt;
    initStyleOption(&opt);
    int w = 0, h = 0;
    if (Qt::ToolButtonIconOnly == opt.toolButtonStyle || Qt::ToolButtonFollowStyle == opt.toolButtonStyle) {
        // 和文本无关
        w = opt.iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right();
        h = opt.iconSize.height() + d_ptr->mMargins.top() + d_ptr->mMargins.bottom();
    } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle || Qt::ToolButtonTextBesideIcon == opt.toolButtonStyle) {
        QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text);
        textSize.setHeight(textSize.height() + 4);
        QSize iconSize = opt.iconSize;
        if (!opt.icon.isNull()) {
            // 有图标，要有iconsize高度的1/4给颜色
            iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing);
        }
        w = textSize.width() + d_ptr->mSpacing + iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right();
        h = qMax(textSize.height(), iconSize.height()) + d_ptr->mMargins.top() + d_ptr->mMargins.bottom();
    } else if (Qt::ToolButtonTextUnderIcon == opt.toolButtonStyle) {
        QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text);
        textSize.setHeight(textSize.height() + 4);
        QSize iconSize = opt.iconSize;
        if (!opt.icon.isNull()) {
            // 有图标，要有iconsize高度的1/4给颜色
            iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing);
        }
        w = qMax(textSize.width(), iconSize.width()) + d_ptr->mMargins.left() + d_ptr->mMargins.right();
        h = textSize.height() + iconSize.height() + d_ptr->mSpacing + d_ptr->mMargins.top() + d_ptr->mMargins.bottom();
    }
    opt.rect.setSize(QSize(w, h));  // PM_MenuButtonIndicator depends on the height
    if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) {
        w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
    }
    //! Qt6.4 取消了QApplication::globalStrut
    return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).expandedTo(QSize(2, 2));
}

void SAColorToolButton::onButtonClicked(bool checked)
{
    Q_EMIT colorClicked(color(), checked);
}

/**
 * @brief 绘制按钮
 * @param p
 * @param opt
 */
void SAColorToolButton::paintButton(QStylePainter* p, const QStyleOptionToolButton& opt)
{
    bool autoRaise = opt.state & QStyle::State_AutoRaise;
    // 绘制按钮
    if (autoRaise) {
        style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p, this);
    } else {
        style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &opt, p, this);
    }
    if (opt.features & QStyleOptionToolButton::MenuButtonPopup) {
        QStyleOption tool = opt;
        tool.state        = d_ptr->getButtonMenuStyleState(opt);
        tool.rect         = d_ptr->getIndicatorRect(opt);
        //        if (tool.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) {
        //            style()->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, &tool, &p, this);
        //        }
        style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, p, this);
    }

    // 绘制focus
    if (opt.state & QStyle::State_HasFocus) {
        QStyleOptionFocusRect fr;
        fr.QStyleOption::operator=(opt);
        fr.rect.adjust(3, 3, -3, -3);
        if (opt.features & QStyleOptionToolButton::MenuButtonPopup) {
            fr.rect.adjust(0, 0, style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this), 0);
        }
        style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, p, this);
    }
}

/**
 * @brief 绘制icon
 * @param p
 * @param iconRect
 * @param opt icon信息从QStyleOptionToolButton获取
 */
void SAColorToolButton::paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt)
{
    if (!iconRect.isNull()) {
        QPixmap pm = d_ptr->createIconPixmap(opt, iconRect);
        style()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pm);
    }
}

/**
 * @brief 绘制文本
 * @param p
 * @param textRect
 * @param opt
 */
void SAColorToolButton::paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt)
{
    if (opt.text.isEmpty()) {
        return;
    }
    if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) {
        return;
    }
    p->save();
    p->setFont(opt.font);
    int alignment = Qt::TextShowMnemonic;
    // 快捷键的下划线
    if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) {
        alignment |= Qt::TextHideMnemonic;
    }
    alignment |= Qt::AlignHCenter | Qt::AlignVCenter;
    style()->drawItemText(p,
                          QStyle::visualRect(opt.direction, opt.rect, textRect),
                          alignment,
                          opt.palette,
                          opt.state & QStyle::State_Enabled,
                          opt.text,
                          QPalette::ButtonText);
    p->restore();
}

/**
 * @brief 绘制color
 * @param p
 * @param iconRect
 * @param opt
 */
void SAColorToolButton::paintColor(QStylePainter* p,
                                   const QRect& colorRect,
                                   const QColor& color,
                                   const QStyleOptionToolButton& opt)
{
    Q_UNUSED(opt);
    if (colorRect.isNull()) {
        return;
    }
    // 绘制颜色
    if (color.isValid()) {
        p->fillRect(colorRect, color);
    } else {
        paintNoneColor(p, colorRect);
    }
}

/*** End of inlined file: SAColorToolButton.cpp ***/

// sa ribbon

/*** Start of inlined file: SARibbonUtil.cpp ***/
#include <QFile>
#include <QWidget>
#include <QDebug>
#include <QDir>
#include <QApplication>
#include <QScreen>
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
#include <QDesktopWidget>
#else
#endif
namespace SA
{

/**
 * @brief makeColorVibrant 让颜色鲜艳
 * @param c 原来的颜色
 * @param saturationDelta 增加饱和度（上限255）
 * @param valueDelta 增加明度（上限255）
 * @return
 */
QColor makeColorVibrant(const QColor& c, int saturationDelta, int valueDelta)
{
    int h, s, v, a;
    c.getHsv(&h, &s, &v, &a);  // 分解HSV分量

    // 增加饱和度（上限255）
    s = qMin(s + saturationDelta, 255);
    // 增加明度（上限255）
    v = qMin(v + valueDelta, 255);

    return QColor::fromHsv(h, s, v, a);  // 重新生成颜色
}

/**
 * @brief 按照指定的新高度，保持宽高比缩放 QSize
 *
 * 此函数根据原始尺寸的宽高比，计算出在指定新高度下的对应宽度，
 * 并返回一个新的 QSize 对象。
 *
 * @param originalSize 原始尺寸。
 * @param newHeight    缩放后的新高度。
 * @return             按比例缩放后的 QSize。
 *
 * @par 示例：
 * @code
 * QSize original(800, 600);
 * QSize scaled = scaleSizeByHeight(original, 300);
 * // scaled 将是 (400, 300)
 * @endcode
 */
QSize scaleSizeByHeight(const QSize& originalSize, int newHeight)
{
    // 检查原始尺寸高度、宽度是否有效，以及目标高度是否有效
    if (originalSize.height() <= 0 || originalSize.width() < 0 || newHeight <= 0) {
        return QSize(0, 0);  // 无效输入返回零尺寸
    }

    // 计算宽高比并缩放
    float aspectRatio = static_cast< float >(originalSize.width()) / static_cast< float >(originalSize.height());
    int newWidth      = static_cast< int >(newHeight * aspectRatio);
    return QSize(newWidth, newHeight);
}

/**
 * @brief 按照指定的新高度，宽高比为1:factor缩放 QSize。
 *
 * 此函数根据原始尺寸的宽高比，计算出在指定新高度下的对应宽度，
 * 并返回一个新的 QSize 对象。
 *
 * @param originalSize 原始尺寸。
 * @param newHeight    缩放后的新高度。
 * @param factor    宽高比 1:factor factor=1时，此函数和scaleSizeByHeight的两参数版本一样，如果factor=0.5，则宽高比为1:0.5，也就是高度扩充2倍，宽度扩充1倍
 * @return             按比例缩放后的 QSize。
 *
 * @par 示例：
 * @code
 * QSize original(800, 600);
 * QSize scaled = scaleSizeByHeight(original, 300, 2);
 * // scaled 将是 (600, 300)
 * @endcode
 */
QSize scaleSizeByHeight(const QSize& originalSize, int newHeight, qreal factor)
{
    // 1. 参数合法性检查
    if (originalSize.height() <= 0 || originalSize.width() < 0 || newHeight <= 0 || qFuzzyIsNull(factor)) {
        return QSize(0, 0);
    }

    // 2. 计算原始宽高比
    const qreal originalAspect = static_cast< qreal >(originalSize.width()) / static_cast< qreal >(originalSize.height());

    // 3. 把用户提供的 1:factor 转换成真正的目标宽高比
    //    目标宽高比 = originalAspect / factor
    const qreal targetAspect = originalAspect / factor;

    // 4. 根据新高度反推宽度
    const int newWidth = qRound(newHeight * targetAspect);

    return QSize(newWidth, newHeight);
}

/**
 * @brief 按照指定的新宽度，保持宽高比缩放 QSize。
 *
 * 此函数根据原始尺寸的宽高比，计算出在指定新宽度下的对应高度，
 * 并返回一个新的 QSize 对象。
 *
 * @param originalSize 原始尺寸。
 * @param newWidth     缩放后的新宽度。
 * @return             按比例缩放后的 QSize。
 *
 * @par 示例：
 * @code
 * QSize original(800, 600);
 * QSize scaled = scaleSizeByWidth(original, 400);
 * // scaled 将是 (400, 300)
 * @endcode
 */
QSize scaleSizeByWidth(const QSize& originalSize, int newWidth)
{
    // 检查原始尺寸宽度、高度是否有效，以及目标宽度是否有效
    if (originalSize.width() <= 0 || originalSize.height() < 0 || newWidth <= 0) {
        return QSize(0, 0);  // 无效输入返回零尺寸
    }

    // 计算高宽比并缩放
    float aspectRatio = static_cast< float >(originalSize.height()) / static_cast< float >(originalSize.width());
    int newHeight     = static_cast< int >(newWidth * aspectRatio);
    return QSize(newWidth, newHeight);
}

/**
 * @brief 获取内置主题对应的qss
 * @param theme
 * @return
 */
QString getBuiltInRibbonThemeQss(SARibbonTheme theme)
{
    QFile file;
    switch (theme) {
    case SARibbonTheme::RibbonThemeWindows7:
        file.setFileName(":/SARibbonTheme/resource/theme-win7.qss");
        break;
    case SARibbonTheme::RibbonThemeOffice2013:
        file.setFileName(":/SARibbonTheme/resource/theme-office2013.qss");
        break;
    case SARibbonTheme::RibbonThemeOffice2016Blue:
        file.setFileName(":/SARibbonTheme/resource/theme-office2016-blue.qss");
        break;
    case SARibbonTheme::RibbonThemeOffice2021Blue:
        file.setFileName(":/SARibbonTheme/resource/theme-office2021-blue.qss");
        break;
    case SARibbonTheme::RibbonThemeDark:
        file.setFileName(":/SARibbonTheme/resource/theme-dark.qss");
        break;
    case SARibbonTheme::RibbonThemeDark2:
        file.setFileName(":/SARibbonTheme/resource/theme-dark2.qss");
        break;
    default:
        file.setFileName(":/SARibbonTheme/resource/theme-office2013.qss");
        break;
    }
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "can not load build in ribbon theme,reason is :" << file.errorString();
        return QString();
    }
    return QString::fromUtf8(file.readAll());
}

/**
 * @brief 设置内置的ribbon主题
 *
 * 之所以提供此函数，是因为在某些情况下，SARibbonBar不用在SARibbonMainWindow情况下的时候，也需要设置主题，
 * 但主题设置函数是SARibbonMainWindow的成员函数，因此，这里单独提供了这个函数给SARibbonWidget窗口使用
 *
 * @param w
 * @param theme
 */
void setBuiltInRibbonTheme(QWidget* w, SARibbonTheme theme)
{
    if (!w) {
        return;
    }
    w->setStyleSheet(getBuiltInRibbonThemeQss(theme));
}

/**
 * @brief 为 QIcon 生成指定 devicePixelRatio 的高分辨率 pixmap
 *
 * 之所以提供此函数，是因为 Qt5 的 QIcon::pixmap() 没有 devicePixelRatio 参数，而Qt6做了适配，为此专门提供一个兼容函数
 *
 * @param icon   图标源
 * @param size   期望的逻辑像素大小（控件坐标系）
 * @param devicePixelRatio    目标屏幕的实时 devicePixelRatio（可用 QWindow::devicePixelRatio() 获取）
 * @param mode   图标模式（Normal/Disabled/Active/Selected）
 * @param state  图标状态（On/Off）
 * @return 已设置好 devicePixelRatio 的 QPixmap，可直接 drawPixmap 使用
 */
QPixmap iconToPixmap(const QIcon& icon, const QSize& size, qreal devicePixelRatio, QIcon::Mode mode, QIcon::State state)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    return icon.pixmap(size, devicePixelRatio, mode, state);
#else
    Q_UNUSED(devicePixelRatio);
    return icon.pixmap(size, mode, state);
#endif
}

/**
 * @brief 获取窗口当前所在屏幕的dpr
 * @param w
 * @return
 */
qreal widgetDevicePixelRatio(QWidget* w)
{
    if (!w) {
        return 1.0;
    }
    // 获取窗口所在的屏幕（优先当前窗口的屏幕）
    QScreen* sc = nullptr;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    if (QDesktopWidget* dw = QApplication::desktop()) {
        int idx = dw->screenNumber(w);  // -1 表示主屏
        if (QScreen* sc = dw->screen(idx)) {
            return sc->devicePixelRatio();
        }
    }
#else
    // 先获取窗口的顶层窗口（避免子部件直接调用screen()可能返回null的问题）
    QWidget* topWidget = w->window();
    if (topWidget) {
        sc = topWidget->screen();
    }
#endif
    if (!sc) {
        // qApp->primaryScreen() 拿到的是“整个系统里被用户标记成 primary 的那一块屏,是“全局主屏”
        sc = QApplication::primaryScreen();
    }
    if (!sc) {
        return 1.0;
    }
    return sc->devicePixelRatio();
}

}

/*** End of inlined file: SARibbonUtil.cpp ***/

/*** Start of inlined file: SAFramelessHelper.cpp ***/
#include <QRect>
#include <QRubberBand>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QApplication>
#include <QDebug>
#include <QScreen>
#include <QWindow>

class SAPrivateFramelessWidgetData;

/*****
 * FramelessHelperPrivate
 * 存储界面对应的数据集合，以及是否可移动、可缩放属性
 *****/
class SAFramelessHelper::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SAFramelessHelper)
    friend class SAPrivateFramelessWidgetData;

public:
    PrivateData(SAFramelessHelper* p);
    QHash< QWidget*, SAPrivateFramelessWidgetData* > m_widgetDataHash;
    bool m_bWidgetMovable { true };
    bool m_bWidgetResizable { true };
    bool m_bRubberBandOnResize { true };
    bool m_bRubberBandOnMove { true };
};

SAFramelessHelper::PrivateData::PrivateData(SAFramelessHelper* p) : q_ptr(p)
{
}

/**
 * @brief 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角。
 *
 * 该类用于计算鼠标相对于窗口边框的位置，以便确定是否可以进行缩放操作。
 */
class SAPrivateFramelessCursorPosCalculator
{
public:
    explicit SAPrivateFramelessCursorPosCalculator();
    // 重置鼠标位置状态
    void reset();
    // 根据全局鼠标位置和窗口矩形重新计算鼠标位置状态。
    void recalculate(const QPoint& globalMousePos, const QRect& frameRect);

public:
    bool mIsOnEdges { true };
    bool mIsOnLeftEdge { true };
    bool mIsOnRightEdge { true };
    bool mIsOnTopEdge { true };
    bool mIsOnBottomEdge { true };
    bool mIsOnTopLeftEdge { true };
    bool mIsOnBottomLeftEdge { true };
    bool mIsOnTopRightEdge { true };
    bool mIsOnBottomRightEdge { true };

    static int s_borderWidth;
    static int s_titleHeight;
};

int SAPrivateFramelessCursorPosCalculator::s_borderWidth = 5;
int SAPrivateFramelessCursorPosCalculator::s_titleHeight = 30;

/***** CursorPosCalculator *****/
SAPrivateFramelessCursorPosCalculator::SAPrivateFramelessCursorPosCalculator()
{
    reset();
}

/**
 * @brief 重置鼠标位置状态。
 *
 * 将所有鼠标位置标志重置为false。
 */
void SAPrivateFramelessCursorPosCalculator::reset()
{
    mIsOnEdges           = false;
    mIsOnLeftEdge        = false;
    mIsOnRightEdge       = false;
    mIsOnTopEdge         = false;
    mIsOnBottomEdge      = false;
    mIsOnTopLeftEdge     = false;
    mIsOnBottomLeftEdge  = false;
    mIsOnTopRightEdge    = false;
    mIsOnBottomRightEdge = false;
}

/**
 * @brief 根据全局鼠标位置和窗口矩形重新计算鼠标位置状态。
 * @param globalMousePos 全局鼠标位置。
 * @param frameRect 窗口的矩形区域。
 */
void SAPrivateFramelessCursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect)
{
    qreal dpiScale        = SAFramelessHelper::getScreenDpiScale(QApplication::widgetAt(gMousePos));
    int scaledBorderWidth = s_borderWidth * dpiScale;
    int globalMouseX      = gMousePos.x();
    int globalMouseY      = gMousePos.y();

    int frameX = frameRect.x();
    int frameY = frameRect.y();

    int frameWidth  = frameRect.width();
    int frameHeight = frameRect.height();

    mIsOnLeftEdge = (globalMouseX >= frameX && globalMouseX <= frameX + scaledBorderWidth);

    mIsOnRightEdge = (globalMouseX >= frameX + frameWidth - scaledBorderWidth && globalMouseX <= frameX + frameWidth);

    mIsOnTopEdge = (globalMouseY >= frameY && globalMouseY <= frameY + scaledBorderWidth);

    mIsOnBottomEdge = (globalMouseY >= frameY + frameHeight - scaledBorderWidth && globalMouseY <= frameY + frameHeight);

    mIsOnTopLeftEdge     = mIsOnTopEdge && mIsOnLeftEdge;
    mIsOnBottomLeftEdge  = mIsOnBottomEdge && mIsOnLeftEdge;
    mIsOnTopRightEdge    = mIsOnTopEdge && mIsOnRightEdge;
    mIsOnBottomRightEdge = mIsOnBottomEdge && mIsOnRightEdge;

    mIsOnEdges = mIsOnLeftEdge || mIsOnRightEdge || mIsOnTopEdge || mIsOnBottomEdge;
}

/**
 * @brief 更新鼠标样式、移动窗体、缩放窗体。
 *
 * 该类负责处理窗口的鼠标事件，包括鼠标移动、按下、释放等，以实现窗口的移动和缩放功能。
 */
class SAPrivateFramelessWidgetData
{
public:
    explicit SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget);
    ~SAPrivateFramelessWidgetData();
    QWidget* widget();

    // 处理鼠标事件-划过、按下、释放、移动
    bool handleWidgetEvent(QEvent* event);

    // 更新橡皮筋状态
    void updateRubberBandStatus();

private:
    // 更新鼠标样式
    void updateCursorShape(const QPoint& gMousePos);

    // 重置窗体大小
    void resizeWidget(const QPoint& gMousePos);

    // 移动窗体
    void moveWidget(const QPoint& gMousePos);

    // 处理鼠标按下
    bool handleMousePressEvent(QMouseEvent* event);

    // 处理鼠标释放
    bool handleMouseReleaseEvent(QMouseEvent* event);

    // 处理鼠标移动
    bool handleMouseMoveEvent(QMouseEvent* event);

    // 处理鼠标离开
    bool handleLeaveEvent(QEvent* event);

    // 处理鼠标进入
    bool handleHoverMoveEvent(QHoverEvent* event);

    // 处理鼠标双击事件
    bool handleDoubleClickedMouseEvent(QMouseEvent* event);

private:
    SAFramelessHelper::PrivateData* d;
    QRubberBand* m_pRubberBand;
    QWidget* m_pWidget;
    QPoint m_ptDragPos;
    SAPrivateFramelessCursorPosCalculator m_pressedMousePos;
    SAPrivateFramelessCursorPosCalculator m_moveMousePos;
    bool m_bLeftButtonPressed;
    bool m_bCursorShapeChanged;
    bool m_bLeftButtonTitlePressed;
    Qt::WindowFlags m_windowFlags;
};

/***** WidgetData *****/
SAPrivateFramelessWidgetData::SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget)
{
    d                         = pd;
    m_pWidget                 = pTopLevelWidget;
    m_bLeftButtonPressed      = false;
    m_bCursorShapeChanged     = false;
    m_bLeftButtonTitlePressed = false;
    m_pRubberBand             = NULL;

    m_windowFlags = m_pWidget->windowFlags();
    m_pWidget->setMouseTracking(true);
    m_pWidget->setAttribute(Qt::WA_Hover, true);

    updateRubberBandStatus();
}

SAPrivateFramelessWidgetData::~SAPrivateFramelessWidgetData()
{
    m_pWidget->setMouseTracking(false);
    m_pWidget->setWindowFlags(m_windowFlags);
    m_pWidget->setAttribute(Qt::WA_Hover, false);

    delete m_pRubberBand;
    m_pRubberBand = NULL;
}

QWidget* SAPrivateFramelessWidgetData::widget()
{
    return (m_pWidget);
}

bool SAPrivateFramelessWidgetData::handleWidgetEvent(QEvent* event)
{
    switch (event->type()) {
    case QEvent::MouseButtonPress:
        return (handleMousePressEvent(static_cast< QMouseEvent* >(event)));

    case QEvent::MouseButtonRelease:
        return (handleMouseReleaseEvent(static_cast< QMouseEvent* >(event)));

    case QEvent::MouseMove:
        return (handleMouseMoveEvent(static_cast< QMouseEvent* >(event)));

    case QEvent::Leave:
        return (handleLeaveEvent(event));

    case QEvent::HoverMove:
        return (handleHoverMoveEvent(static_cast< QHoverEvent* >(event)));

    case QEvent::MouseButtonDblClick:
        return (handleDoubleClickedMouseEvent(static_cast< QMouseEvent* >(event)));

    default:
        break;
    }
    return (false);
}

void SAPrivateFramelessWidgetData::updateRubberBandStatus()
{
    if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) {
        if (NULL == m_pRubberBand) {
            m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
        }
    } else {
        delete m_pRubberBand;
        m_pRubberBand = NULL;
    }
}

void SAPrivateFramelessWidgetData::updateCursorShape(const QPoint& gMousePos)
{
    if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) {
        if (m_bCursorShapeChanged) {
            m_pWidget->unsetCursor();
        }
        return;
    }

    m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());

    if (m_moveMousePos.mIsOnTopLeftEdge || m_moveMousePos.mIsOnBottomRightEdge) {
        m_pWidget->setCursor(Qt::SizeFDiagCursor);
        m_bCursorShapeChanged = true;
    } else if (m_moveMousePos.mIsOnTopRightEdge || m_moveMousePos.mIsOnBottomLeftEdge) {
        m_pWidget->setCursor(Qt::SizeBDiagCursor);
        m_bCursorShapeChanged = true;
    } else if (m_moveMousePos.mIsOnLeftEdge || m_moveMousePos.mIsOnRightEdge) {
        m_pWidget->setCursor(Qt::SizeHorCursor);
        m_bCursorShapeChanged = true;
    } else if (m_moveMousePos.mIsOnTopEdge || m_moveMousePos.mIsOnBottomEdge) {
        m_pWidget->setCursor(Qt::SizeVerCursor);
        m_bCursorShapeChanged = true;
    } else {
        if (m_bCursorShapeChanged) {
            m_pWidget->unsetCursor();
            m_bCursorShapeChanged = false;
        }
    }
}

void SAPrivateFramelessWidgetData::resizeWidget(const QPoint& gMousePos)
{
    QRect origRect;
    if (d->m_bRubberBandOnResize) {
        origRect = m_pRubberBand->frameGeometry();
    } else {
        origRect = m_pWidget->frameGeometry();
    }

    int left   = origRect.left();
    int top    = origRect.top();
    int right  = origRect.right();
    int bottom = origRect.bottom();

    origRect.getCoords(&left, &top, &right, &bottom);

    int minWidth  = m_pWidget->minimumWidth();
    int minHeight = m_pWidget->minimumHeight();

    if (m_pressedMousePos.mIsOnTopLeftEdge) {
        left = gMousePos.x();
        top  = gMousePos.y();
    } else if (m_pressedMousePos.mIsOnBottomLeftEdge) {
        left   = gMousePos.x();
        bottom = gMousePos.y();
    } else if (m_pressedMousePos.mIsOnTopRightEdge) {
        right = gMousePos.x();
        top   = gMousePos.y();
    } else if (m_pressedMousePos.mIsOnBottomRightEdge) {
        right  = gMousePos.x();
        bottom = gMousePos.y();
    } else if (m_pressedMousePos.mIsOnLeftEdge) {
        left = gMousePos.x();
    } else if (m_pressedMousePos.mIsOnRightEdge) {
        right = gMousePos.x();
    } else if (m_pressedMousePos.mIsOnTopEdge) {
        top = gMousePos.y();
    } else if (m_pressedMousePos.mIsOnBottomEdge) {
        bottom = gMousePos.y();
    }

    QRect newRect(QPoint(left, top), QPoint(right, bottom));

    if (newRect.isValid()) {
        if (minWidth > newRect.width()) {
            if (left != origRect.left()) {
                newRect.setLeft(origRect.left());
            } else {
                newRect.setRight(origRect.right());
            }
        }
        if (minHeight > newRect.height()) {
            if (top != origRect.top()) {
                newRect.setTop(origRect.top());
            } else {
                newRect.setBottom(origRect.bottom());
            }
        }

        if (d->m_bRubberBandOnResize) {
            m_pRubberBand->setGeometry(newRect);
        } else {
            m_pWidget->setGeometry(newRect);
        }
    }
}

void SAPrivateFramelessWidgetData::moveWidget(const QPoint& gMousePos)
{
    if (d->m_bRubberBandOnMove) {
        m_pRubberBand->move(gMousePos - m_ptDragPos);
    } else {
        m_pWidget->move(gMousePos - m_ptDragPos);
    }
}

/**
 * @brief 处理鼠标事件-划过、按下、释放、移动。
 * @param event 要处理的事件指针。
 * @return 如果事件被处理，则返回true；否则返回false。
 */
bool SAPrivateFramelessWidgetData::handleMousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton) {
        m_bLeftButtonPressed = true;

        // m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.s_titleHeight;
        qreal dpiScale            = SAFramelessHelper::getScreenDpiScale(m_pWidget);
        int scaledTitleHeight     = SAPrivateFramelessCursorPosCalculator::s_titleHeight * dpiScale;
        m_bLeftButtonTitlePressed = event->pos().y() < scaledTitleHeight;

        QRect frameRect = m_pWidget->frameGeometry();
        auto gp         = SA_MOUSEEVENT_GLOBALPOS_POINT(event);
        m_pressedMousePos.recalculate(gp, frameRect);

        m_ptDragPos = gp - frameRect.topLeft();

        if (m_pressedMousePos.mIsOnEdges) {
            if (m_pWidget->isMaximized()) {
                // 窗口在最大化状态时，点击边界不做任何处理
                return (false);
            }
            if (d->m_bRubberBandOnResize) {
                m_pRubberBand->setGeometry(frameRect);
                m_pRubberBand->show();
                return (true);
            }
        } else if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed) {
            if (m_pWidget->isMaximized()) {
                // 窗口在最大化状态时，点击标题栏不做任何处理
                return (false);
            }
            m_pRubberBand->setGeometry(frameRect);
            m_pRubberBand->show();
            return (true);
        }
    }
    return (false);
}

bool SAPrivateFramelessWidgetData::handleMouseReleaseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton) {
        m_bLeftButtonPressed      = false;
        m_bLeftButtonTitlePressed = false;
        m_pressedMousePos.reset();
        if (m_pRubberBand && m_pRubberBand->isVisible()) {
            m_pRubberBand->hide();
            m_pWidget->setGeometry(m_pRubberBand->geometry());
            return (true);
        }
    }
    return (false);
}

bool SAPrivateFramelessWidgetData::handleMouseMoveEvent(QMouseEvent* event)
{
    QPoint p = SA_MOUSEEVENT_GLOBALPOS_POINT(event);
    if (m_bLeftButtonPressed) {
        if (d->m_bWidgetResizable && m_pressedMousePos.mIsOnEdges) {
            if (m_pWidget->isMaximized()) {
                // 窗口在最大化状态时，点击边界不做任何处理
                return (false);
            }
            resizeWidget(p);
            return (true);
        } else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed) {
            if (m_pWidget->isMaximized()) {
                // 先求出窗口到鼠标的相对位置
                QRect normalGeometry = m_pWidget->normalGeometry();
                m_pWidget->showNormal();
                // 修改前：
                // p.ry() -= 10;
                // p.rx() -= (normalGeometry.width() / 2);

                // 修改后：使用DPI缩放偏移量
                qreal dpiScale = SAFramelessHelper::getScreenDpiScale(m_pWidget);
                p.ry() -= 10 * dpiScale;  // 缩放偏移量
                p.rx() -= (normalGeometry.width() / 2);

                m_pWidget->move(p);
                // 这时要重置m_ptDragPos
                m_ptDragPos = QPoint(normalGeometry.width() / 2, 10);
                return (true);
            }
            moveWidget(p);
            return (true);
        }
        return (false);
    } else if (d->m_bWidgetResizable) {
        updateCursorShape(p);
    }
    return (false);
}

bool SAPrivateFramelessWidgetData::handleLeaveEvent(QEvent* event)
{
    Q_UNUSED(event)
    if (!m_bLeftButtonPressed) {
        m_pWidget->unsetCursor();
        return (true);
    }
    return (false);
}

bool SAPrivateFramelessWidgetData::handleHoverMoveEvent(QHoverEvent* event)
{
    if (d->m_bWidgetResizable) {
        updateCursorShape(m_pWidget->mapToGlobal(SA_HOVEREVENT_POS_POINT(event)));
    }
    return (false);
}

bool SAPrivateFramelessWidgetData::handleDoubleClickedMouseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton) {
        if (m_pWidget) {
            SARibbonMainWindow* mainwindow = qobject_cast< SARibbonMainWindow* >(m_pWidget);
            if (mainwindow) {
                if (mainwindow->windowFlags() & Qt::WindowMaximizeButtonHint) {
                    // 在最大化按钮显示时才进行shownormal处理

                    // 修改前：
                    // bool titlePressed = event->pos().y() < m_moveMousePos.s_titleHeight;

                    // 修改后：考虑DPI缩放
                    qreal dpiScale        = SAFramelessHelper::getScreenDpiScale(m_pWidget);
                    int scaledTitleHeight = SAPrivateFramelessCursorPosCalculator::s_titleHeight * dpiScale;
                    bool titlePressed     = event->pos().y() < scaledTitleHeight;

                    if (titlePressed) {
                        if (m_pWidget->isMaximized()) {
                            m_pWidget->showNormal();
                        } else {
                            m_pWidget->showMaximized();
                        }
                        return (true);
                    }
                }
            }
        }
    }
    return (false);
}

//===================================================
// SAFramelessHelper
//===================================================
SAFramelessHelper::SAFramelessHelper(QObject* parent) : QObject(parent), d_ptr(new SAFramelessHelper::PrivateData(this))
{
    d_ptr->m_bWidgetMovable      = true;
    d_ptr->m_bWidgetResizable    = true;
    d_ptr->m_bRubberBandOnResize = false;
    d_ptr->m_bRubberBandOnMove   = false;
    if (parent) {
        QWidget* w = qobject_cast< QWidget* >(parent);
        if (w) {
            w->setWindowFlags(w->windowFlags() | Qt::FramelessWindowHint);
            setWidgetMovable(true);       // 设置窗体可移动
            setWidgetResizable(true);     // 设置窗体可缩放
            setRubberBandOnMove(false);   // 设置橡皮筋效果-可移动
            setRubberBandOnResize(true);  // 设置橡皮筋效果-可缩放
            activateOn(w);                // 激活当前窗体
        }
    }
}

SAFramelessHelper::~SAFramelessHelper()
{
    QList< QWidget* > keys = d_ptr->m_widgetDataHash.keys();
    int size               = keys.size();

    for (int i = 0; i < size; ++i) {
        delete d_ptr->m_widgetDataHash.take(keys[ i ]);
    }
}

bool SAFramelessHelper::eventFilter(QObject* obj, QEvent* event)
{
    switch (event->type()) {
    case QEvent::MouseMove:
    case QEvent::HoverMove:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
    case QEvent::Leave: {
        SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.value(static_cast< QWidget* >(obj));
        if (data) {
            return (data->handleWidgetEvent(event));
        }
        break;
    }

    default:
        break;
    }
    return (QObject::eventFilter(obj, event));
}

/**
 * @brief 激活指定的顶级窗体
 * 使指定的顶级窗体能够使用 SAFramelessHelper 提供的无边框移动和缩放功能。
 * @param topLevelWidget 要激活的顶级窗体指针。
 */
void SAFramelessHelper::activateOn(QWidget* topLevelWidget)
{
    if (!d_ptr->m_widgetDataHash.contains(topLevelWidget)) {
        SAPrivateFramelessWidgetData* data = new SAPrivateFramelessWidgetData(d_ptr.get(), topLevelWidget);
        d_ptr->m_widgetDataHash.insert(topLevelWidget, data);

        topLevelWidget->installEventFilter(this);
    }
}

/**
 * @brief 从指定的顶级窗体移除帮助功能
 * 停止对指定顶级窗体的无边框移动和缩放功能支持。
 * @param topLevelWidget 要移除功能的顶级窗体指针。
 */
void SAFramelessHelper::removeFrom(QWidget* topLevelWidget)
{
    SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.take(topLevelWidget);

    if (data) {
        topLevelWidget->removeEventFilter(this);
        delete data;
    }
}

void SAFramelessHelper::setRubberBandOnMove(bool movable)
{
    d_ptr->m_bRubberBandOnMove                        = movable;
    const QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values();

    for (SAPrivateFramelessWidgetData* data : list) {
        data->updateRubberBandStatus();
    }
}

void SAFramelessHelper::setWidgetMovable(bool movable)
{
    d_ptr->m_bWidgetMovable = movable;
}

void SAFramelessHelper::setWidgetResizable(bool resizable)
{
    d_ptr->m_bWidgetResizable = resizable;
}

void SAFramelessHelper::setRubberBandOnResize(bool resizable)
{
    d_ptr->m_bRubberBandOnResize                      = resizable;
    const QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values();

    for (SAPrivateFramelessWidgetData* data : list) {
        data->updateRubberBandStatus();
    }
}

void SAFramelessHelper::setBorderWidth(int width)
{
    if (width > 0) {
        SAPrivateFramelessCursorPosCalculator::s_borderWidth = width;
    }
}

void SAFramelessHelper::setTitleHeight(int height)
{
    if (height > 0) {
        SAPrivateFramelessCursorPosCalculator::s_titleHeight = height;
    }
}

bool SAFramelessHelper::widgetMovable()
{
    return (d_ptr->m_bWidgetMovable);
}

bool SAFramelessHelper::widgetResizable()
{
    return (d_ptr->m_bWidgetResizable);
}

bool SAFramelessHelper::rubberBandOnMove()
{
    return (d_ptr->m_bRubberBandOnMove);
}

bool SAFramelessHelper::rubberBandOnResisze()
{
    return (d_ptr->m_bRubberBandOnResize);
}

uint SAFramelessHelper::borderWidth()
{
    return (SAPrivateFramelessCursorPosCalculator::s_borderWidth);
}

uint SAFramelessHelper::titleHeight()
{
    return (SAPrivateFramelessCursorPosCalculator::s_titleHeight);
}

qreal SAFramelessHelper::getScreenDpiScale(const QWidget* widget)
{
    if (!widget) {
        if (QApplication::primaryScreen()) {
            return QApplication::primaryScreen()->devicePixelRatio();
        }
        return 1.0;
    }

    QWindow* window = widget->windowHandle();
    if (!window) {
        const QWidget* nativeParent = widget->nativeParentWidget();
        if (nativeParent) {
            window = nativeParent->windowHandle();
        }
    }

    if (window && window->screen()) {
        return window->screen()->devicePixelRatio();
    }

    if (QApplication::primaryScreen()) {
        return QApplication::primaryScreen()->devicePixelRatio();
    }

    return 1.0;
}

/*** End of inlined file: SAFramelessHelper.cpp ***/

/*** Start of inlined file: SARibbonApplicationButton.cpp ***/
SARibbonApplicationButton::SARibbonApplicationButton(QWidget* parent) : QToolButton(parent)
{
    setFocusPolicy(Qt::NoFocus);
    setAutoRaise(true);
    setPopupMode(QToolButton::InstantPopup);
    setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setMinimumWidth(40);
}

SARibbonApplicationButton::SARibbonApplicationButton(const QString& text, QWidget* parent) : QToolButton(parent)
{
    setFocusPolicy(Qt::NoFocus);
    setAutoRaise(true);
    setPopupMode(QToolButton::InstantPopup);
    setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setText(text);
    setMinimumWidth(40);
}

SARibbonApplicationButton::SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent)
    : QToolButton(parent)
{
    setFocusPolicy(Qt::NoFocus);
    setAutoRaise(true);
    setPopupMode(QToolButton::InstantPopup);
    setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setIcon(icon);
    setText(text);
    setMinimumWidth(40);
}

SARibbonApplicationButton::~SARibbonApplicationButton()
{
}

/*** End of inlined file: SARibbonApplicationButton.cpp ***/

/*** Start of inlined file: SARibbonSystemButtonBar.cpp ***/
#include <QToolButton>
#include <QResizeEvent>
#include <QStyle>
#include <QDebug>
#include <QScopedPointer>
#include <QWindowStateChangeEvent>

// 为了避免使用此框架的app设置了全局的qpushbutton 的 qss样式影响此按钮，定义了一个类

/**
 * @brief The SARibbonSystemButtonBar class
 */
class SARibbonSystemButtonBar::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonSystemButtonBar)
public:
    SARibbonSystemToolButton* buttonClose { nullptr };
    SARibbonSystemToolButton* buttonMinimize { nullptr };
    SARibbonSystemToolButton* buttonMaximize { nullptr };
    int mCloseStretch { 4 };
    int mMaxStretch { 3 };
    int mMinStretch { 3 };
    int mWindowButtonWidth { 35 };
    int mTitleBarHeight { 28 };
    Qt::WindowFlags mFlags { Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint };
    SARibbonButtonGroupWidget* mButtonGroup;

public:
    PrivateData(SARibbonSystemButtonBar* p) : q_ptr(p)
    {
        mButtonGroup = new SARibbonButtonGroupWidget(p);
        mButtonGroup->setObjectName("SASystemButtonGroup");
    }

    void setupMinimizeButton(bool on)
    {
        SARibbonSystemButtonBar* par = q_ptr;

        if (on) {
            if (buttonMinimize) {
                buttonMinimize->deleteLater();
                buttonMinimize = nullptr;
            }
            buttonMinimize = new SARibbonSystemToolButton(par);
            buttonMinimize->setObjectName(QStringLiteral("SAMinimizeWindowButton"));
            buttonMinimize->setFocusPolicy(Qt::NoFocus);  // 避免铺抓到
            buttonMinimize->show();
            par->connect(buttonMinimize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::minimizeWindow);
        } else {
            if (buttonMinimize) {
                buttonMinimize->deleteLater();
                buttonMinimize = nullptr;
            }
        }
        updateSize();
    }

    void setupMaximizeButton(bool on)
    {
        SARibbonSystemButtonBar* par = q_ptr;

        if (on) {
            if (buttonMaximize) {
                buttonMaximize->deleteLater();
                buttonMaximize = nullptr;
            }
            buttonMaximize = new SARibbonSystemToolButton(par);
            buttonMaximize->setObjectName(QStringLiteral("SAMaximizeWindowButton"));
            buttonMaximize->setCheckable(true);
            buttonMaximize->setFocusPolicy(Qt::NoFocus);  // 避免铺抓到
            //            buttonMaximize->setIconSize(buttonMaximize->size() * mIconscale);
            buttonMaximize->show();
            par->connect(buttonMaximize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::maximizeWindow);
        } else {
            if (buttonMaximize) {
                buttonMaximize->deleteLater();
                ;
                buttonMaximize = nullptr;
            }
        }
        updateSize();
    }

    void setupCloseButton(bool on)
    {
        SARibbonSystemButtonBar* par = q_ptr;

        if (on) {
            if (buttonClose) {
                buttonClose->deleteLater();
                buttonClose = nullptr;
            }
            buttonClose = new SARibbonSystemToolButton(par);
            buttonClose->setObjectName(QStringLiteral("SACloseWindowButton"));
            buttonClose->setFocusPolicy(Qt::NoFocus);  // 避免铺抓到
            // buttonClose->setFlat(true);
            par->connect(buttonClose, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::closeWindow);
            //            buttonClose->setIconSize(buttonClose->size() * mIconscale);
            buttonClose->show();
        } else {
            if (buttonClose) {
                buttonClose->deleteLater();
                ;
                buttonClose = nullptr;
            }
        }
        updateSize();
    }

    void updateSize()
    {
        resizeElement(q_ptr->size());
    }

    void resizeElement(QSize size)
    {
        int x = size.width();
        if (buttonClose) {
            int w = closeButtonWidthHint();
            x -= w;
            buttonClose->setGeometry(x, 0, w, size.height());
        }
        if (buttonMaximize) {
            int w = maxButtonWidthHint();
            x -= w;
            buttonMaximize->setGeometry(x, 0, w, size.height());
        }
        if (buttonMinimize) {
            int w = minButtonWidthHint();
            x -= w;
            buttonMinimize->setGeometry(x, 0, w, size.height());
        }
        if (mButtonGroup) {
            mButtonGroup->setGeometry(0, 0, x, size.height());
        }
    }

    int closeButtonWidthHint() const
    {
        qreal t = mCloseStretch + mMaxStretch + mMinStretch;
        return (mCloseStretch * (3 * mWindowButtonWidth)) / t;
    }

    int maxButtonWidthHint() const
    {
        qreal t = mCloseStretch + mMaxStretch + mMinStretch;
        return (mMaxStretch * (3 * mWindowButtonWidth)) / t;
    }

    int minButtonWidthHint() const
    {
        qreal t = mCloseStretch + mMaxStretch + mMinStretch;
        return (mMinStretch * (3 * mWindowButtonWidth)) / t;
    }

    QSize sizeHint() const
    {
        int height = mTitleBarHeight;
        if (height < 20) {
            height = 20;
        }
        QSize res(0, 0);
        if (mButtonGroup) {
            res = mButtonGroup->sizeHint();
        }
        res.setHeight(height);
        if (buttonClose) {
            res.setWidth(res.width() + closeButtonWidthHint());
        }
        if (buttonMaximize) {
            res.setWidth(res.width() + maxButtonWidthHint());
        }
        if (buttonMinimize) {
            res.setWidth(res.width() + minButtonWidthHint());
        }
        return res;
    }
};

//===================================================
// SARibbonSystemToolButton
//===================================================
SARibbonSystemToolButton::SARibbonSystemToolButton(QWidget* p) : QToolButton(p)
{
    setAutoRaise(true);
}
//===================================================
// SARibbonSystemButtonBar
//===================================================
SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent)
    : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this))
{
    updateWindowFlag();
}

/**
 * @brief 构造函数，强制使用flags，而不是用parent的flags进行构造
 * @param parent
 * @param flags
 */
SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags)
    : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this))
{
    d_ptr->mFlags = flags;
    updateWindowFlag();
}

SARibbonSystemButtonBar::~SARibbonSystemButtonBar()
{
}

void SARibbonSystemButtonBar::setupMinimizeButton(bool on)
{
    d_ptr->setupMinimizeButton(on);
}

void SARibbonSystemButtonBar::setupMaximizeButton(bool on)
{
    d_ptr->setupMaximizeButton(on);
}

void SARibbonSystemButtonBar::setupCloseButton(bool on)
{
    d_ptr->setupCloseButton(on);
}

void SARibbonSystemButtonBar::updateWindowFlag()
{
    QWidget* topedWidget = this;
    // 找到最顶层窗口
    while (topedWidget->parentWidget()) {
        topedWidget = topedWidget->parentWidget();
    }
    Qt::WindowFlags flags = topedWidget->windowFlags();
    updateWindowFlag(flags);
}

/**
 * @brief 此函数仅用于控制最小最大化和关闭按钮的显示
 * @param flags
 */
void SARibbonSystemButtonBar::updateWindowFlag(Qt::WindowFlags flags)
{
    d_ptr->mFlags = flags;
    setupMinimizeButton(flags & Qt::WindowMinimizeButtonHint);
    setupMaximizeButton(flags & Qt::WindowMaximizeButtonHint);
    setupCloseButton(flags & Qt::WindowCloseButtonHint);
}

/**
 * @brief 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置
 * @param close 关闭按钮比例
 * @param max 最大化按钮比例
 * @param min 最小化按钮比例
 */
void SARibbonSystemButtonBar::setButtonWidthStretch(int close, int max, int min)
{
    d_ptr->mMaxStretch   = max;
    d_ptr->mMinStretch   = min;
    d_ptr->mCloseStretch = close;
}

/**
 * @brief 标题栏高度
 *
 * 标题栏高度会影响sizeHint
 * @param h
 */
void SARibbonSystemButtonBar::setWindowTitleHeight(int h)
{
    d_ptr->mTitleBarHeight = h;
}

/**
 * @brief 标题栏高度
 * @return
 */
int SARibbonSystemButtonBar::windowTitleHeight() const
{
    return d_ptr->mTitleBarHeight;
}

/**
 * @brief 系统按钮的宽度
 * @param w
 */
void SARibbonSystemButtonBar::setWindowButtonWidth(int w)
{
    d_ptr->mWindowButtonWidth = w;
}

/**
 * @brief 系统按钮的宽度
 * @param w
 */
int SARibbonSystemButtonBar::windowButtonWidth() const
{
    return d_ptr->mWindowButtonWidth;
}

/**
 * @brief 设置窗口状态（最大最小化按钮状态）
 * @param s
 */
void SARibbonSystemButtonBar::setWindowStates(Qt::WindowStates s)
{
    if (d_ptr->buttonMaximize) {
        bool on = s.testFlag(Qt::WindowMaximized);
        d_ptr->buttonMaximize->setChecked(on);
        d_ptr->buttonMaximize->setToolTip(on ? tr("Restore") : tr("Maximize"));
    }
}

/**
 * @brief 此函数返回的flags仅包括 Qt::WindowCloseButtonHint，Qt::WindowMaximizeButtonHint，Qt::WindowMinimizeButtonHint
 * 三个
 *
 * @return
 */
Qt::WindowFlags SARibbonSystemButtonBar::windowButtonFlags() const
{
    Qt::WindowFlags f = Qt::Widget;  // widget是000

    if (d_ptr->mFlags & Qt::WindowCloseButtonHint) {
        f |= Qt::WindowCloseButtonHint;
    }
    if (d_ptr->mFlags & Qt::WindowMaximizeButtonHint) {
        f |= Qt::WindowMaximizeButtonHint;
    }
    if (d_ptr->mFlags & Qt::WindowMinimizeButtonHint) {
        f |= Qt::WindowMinimizeButtonHint;
    }

    return (f);
}

QSize SARibbonSystemButtonBar::sizeHint() const
{
    return (d_ptr->sizeHint());
}

bool SARibbonSystemButtonBar::eventFilter(QObject* obj, QEvent* event)
{
    if (obj && event) {
        SARibbonMainWindow* mainWindow = qobject_cast< SARibbonMainWindow* >(obj);
        if (!mainWindow) {
            // 所有事件都不消费
            return QFrame::eventFilter(obj, event);
        }
        // SARibbonMainWindow的事件
        switch (event->type()) {
        case QEvent::Resize: {
            int th = 25;

            SARibbonBar* ribbonBar = mainWindow->ribbonBar();
            if (ribbonBar) {
                th = ribbonBar->titleBarHeight();
            }
            if (th != height()) {
                setWindowTitleHeight(th);
            }
            QRect fr         = mainWindow->geometry();
            QSize wgSizeHint = sizeHint();
            setGeometry(fr.width() - wgSizeHint.width(), 0, wgSizeHint.width(), wgSizeHint.height());
            // 把设置好的尺寸给ribbonbar
            if (ribbonBar) {
                ribbonBar->setSystemButtonGroupSize(size());
            }
        } break;
        case QEvent::WindowStateChange: {
            setWindowStates(mainWindow->windowState());
        } break;
        default:
            break;
        }
    }
    return QFrame::eventFilter(obj, event);
}

QAbstractButton* SARibbonSystemButtonBar::minimizeButton() const
{
    return d_ptr->buttonMinimize;
}

QAbstractButton* SARibbonSystemButtonBar::maximizeButton() const
{
    return d_ptr->buttonMaximize;
}

QAbstractButton* SARibbonSystemButtonBar::closeButton() const
{
    return d_ptr->buttonClose;
}

void SARibbonSystemButtonBar::setIconSize(const QSize& ic)
{
    d_ptr->mButtonGroup->setIconSize(ic);
}

QSize SARibbonSystemButtonBar::iconSize() const
{
    return d_ptr->mButtonGroup->iconSize();
}

void SARibbonSystemButtonBar::addAction(QAction* a)
{
    d_ptr->mButtonGroup->addAction(a);
}

void SARibbonSystemButtonBar::addMenuAction(QAction* menuAction, QToolButton::ToolButtonPopupMode popupMode)
{
    d_ptr->mButtonGroup->addMenuAction(menuAction, popupMode);
}

QAction* SARibbonSystemButtonBar::addMenuAction(QMenu* menu, QToolButton::ToolButtonPopupMode popupMode)
{
    return d_ptr->mButtonGroup->addMenuAction(menu, popupMode);
}

QAction* SARibbonSystemButtonBar::addSeparator()
{
    return d_ptr->mButtonGroup->addSeparator();
}

QAction* SARibbonSystemButtonBar::addWidget(QWidget* w)
{
    return d_ptr->mButtonGroup->addWidget(w);
}

void SARibbonSystemButtonBar::resizeEvent(QResizeEvent* e)
{
    Q_UNUSED(e);
    d_ptr->resizeElement(size());
}

void SARibbonSystemButtonBar::closeWindow()
{
    if (parentWidget()) {
        parentWidget()->close();
    }
}

void SARibbonSystemButtonBar::minimizeWindow()
{
    if (parentWidget()) {
        parentWidget()->showMinimized();
    }
}

void SARibbonSystemButtonBar::maximizeWindow()
{
    QWidget* par = parentWidget();

    if (par) {
        if (par->isMaximized()) {
            par->showNormal();
        } else {
            par->showMaximized();
        }
    }
}

/*** End of inlined file: SARibbonSystemButtonBar.cpp ***/

/*** Start of inlined file: SARibbonToolButton.cpp ***/
#include <QAction>
#include <QApplication>
#include <QCursor>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QStyleOption>
#include <QStyleOptionFocusRect>
#include <QStyleOptionToolButton>
#include <QStylePainter>
#include <QTextOption>
#include <QApplication>
#include <QScreen>
#include <QProxyStyle>

/**
 * @def 开启此宏会打印一些常见信息
 */
#ifndef SA_RIBBON_TOOLBUTTON_DEBUG_PRINT
#define SA_RIBBON_TOOLBUTTON_DEBUG_PRINT 0
#endif

#ifndef SARIBBONTOOLBUTTON_DEBUG_DRAW
#define SARIBBONTOOLBUTTON_DEBUG_DRAW 0
#endif

#if SARIBBONTOOLBUTTON_DEBUG_DRAW
#ifndef SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT
#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect)                                                                    \
    do {                                                                                                               \
        p.save();                                                                                                      \
        p.setPen(Qt::red);                                                                                             \
        p.setBrush(QBrush());                                                                                          \
        p.drawRect(rect);                                                                                              \
        p.restore();                                                                                                   \
    } while (0)
#endif
#else
#ifndef SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT
#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect)
#endif
#endif
namespace SA
{

QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt)
{
    debug << "==============" << "\nQStyleOption(" << (QStyleOption)opt << ")"
          << "\n  QStyleOptionComplex:"
             "\n     subControls("
          << opt.subControls
          << " ) "
             "\n     activeSubControls("
          << opt.activeSubControls
          << "\n  QStyleOptionToolButton"
             "\n     features("
          << opt.features
          << ")"
             "\n     toolButtonStyle("
          << opt.toolButtonStyle << ")";

    return (debug);
}
}

//===================================================
// SARibbonToolButtonProxyStyle
//===================================================

class SARibbonToolButtonProxyStyle : public QProxyStyle
{
public:
    void drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* widget = nullptr) const override
    {
        if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown || pe == PE_IndicatorArrowRight
            || pe == PE_IndicatorArrowLeft) {
            if (opt->rect.width() <= 1 || opt->rect.height() <= 1) {
                return;
            }

            QRect r  = opt->rect;
            int size = qMin(r.height(), r.width());
            QPixmap pixmap;
            qreal pixelRatio = p->device()->devicePixelRatio();
            int border       = qRound(pixelRatio * (size / 4));
            int sqsize       = qRound(pixelRatio * (2 * (size / 2)));
            QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied);
            image.fill(Qt::transparent);
            QPainter imagePainter(&image);

            QPolygon a;
            switch (pe) {
            case PE_IndicatorArrowUp:
                a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2);
                break;
            case PE_IndicatorArrowDown:
                a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2);
                break;
            case PE_IndicatorArrowRight:
                a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border);
                break;
            case PE_IndicatorArrowLeft:
                a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border);
                break;
            default:
                break;
            }

            int bsx = 0;
            int bsy = 0;

            if (opt->state & State_Sunken) {
                bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget);
                bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget);
            }

            QRect bounds = a.boundingRect();
            int sx       = sqsize / 2 - bounds.center().x() - 1;
            int sy       = sqsize / 2 - bounds.center().y() - 1;
            imagePainter.translate(sx + bsx, sy + bsy);
            imagePainter.setPen(QPen(opt->palette.buttonText().color(), 1.4));
            imagePainter.setBrush(Qt::NoBrush);

            if (!(opt->state & State_Enabled)) {
                imagePainter.translate(1, 1);
                imagePainter.setPen(QPen(opt->palette.light().color(), 1.4));
                imagePainter.drawPolyline(a);
                imagePainter.translate(-1, -1);
                imagePainter.setPen(QPen(opt->palette.mid().color(), 1.4));
            }

            imagePainter.drawPolyline(a);
            imagePainter.end();
            pixmap = QPixmap::fromImage(image);
            pixmap.setDevicePixelRatio(pixelRatio);

            int xOffset = r.x() + (r.width() - size) / 2;
            int yOffset = r.y() + (r.height() - size) / 2;
            p->drawPixmap(xOffset, yOffset, pixmap);
        } else {
            QProxyStyle::drawPrimitive(pe, opt, p, widget);
        }
    }
};

//===================================================
// SARibbonToolButton::PrivateData
//===================================================

class SARibbonToolButton::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonToolButton)
public:
    PrivateData(SARibbonToolButton* p);
    // 根据鼠标位置更新按钮的信息
    void updateStatusByMousePosition(const QPoint& pos);
    // 更新绘图相关的尺寸
    void updateDrawRect(const QStyleOptionToolButton& opt);
    // 更新SizeHint
    void updateSizeHint(const QStyleOptionToolButton& opt);
    // 计算涉及到的rect尺寸
    void calcDrawRects(const QStyleOptionToolButton& opt,
                       QRect& iconRect,
                       QRect& textRect,
                       QRect& indicatorArrowRect,
                       int spacing,
                       int indicatorLen) const;
    // 计算小按钮模式下的尺寸
    void calcSmallButtonDrawRects(const QStyleOptionToolButton& opt,
                                  QRect& iconRect,
                                  QRect& textRect,
                                  QRect& indicatorArrowRect,
                                  int spacing,
                                  int indicatorLen) const;
    // 计算大按钮模式下的尺寸
    void calcLargeButtonDrawRects(const QStyleOptionToolButton& opt,
                                  QRect& iconRect,
                                  QRect& textRect,
                                  QRect& indicatorArrowRect,
                                  int spacing,
                                  int indicatorLen) const;

    // 根据按钮的尺寸调节iconsize(注意这里的buttonRect是已经减去mSpacing的情况)
    QSize adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const;
    // 判断是否有Indicator
    bool hasIndicator(const QStyleOptionToolButton& opt) const;
    // 计算sizehint
    QSize calcSizeHint(const QStyleOptionToolButton& opt);
    QSize calcSmallButtonSizeHint(const QStyleOptionToolButton& opt);
    QSize calcLargeButtonSizeHint(const QStyleOptionToolButton& opt);

    // 计算文本绘制矩形的高度
    int calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const;
    // 估算一个最优的文本宽度
    int estimateLargeButtonTextWidth(int buttonHeight,
                                     int textDrawRectHeight,
                                     const QString& text,
                                     const QFontMetrics& fm,
                                     int maxTrycount = 3);
    QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const;
    // 获取文字的对其方式
    int getTextAlignment() const;
    // 确认文字是否确切要换行显示
    bool isTextNeedWrap() const;
    // 获取真实的icon尺寸
    QSize realIconSize() const;
    // 仅仅对\n进行剔除，和QString::simplified不一样
    static QString simplifiedForRibbonButton(const QString& str);

public:
    bool mMouseOnSubControl { false };  ///< 这个用于标记MenuButtonPopup模式下，鼠标在文本区域
    bool mMenuButtonPressed { false };  ///< 由于Indicator改变，因此hitButton不能用QToolButton的hitButton
    bool mWordWrap { true };            ///< 标记是否文字换行 @default false
    SARibbonToolButton::RibbonButtonType mButtonType { SARibbonToolButton::LargeButton };
    int mSpacing { 1 };                                      ///< 按钮和边框的距离
    int mIndicatorLen { 8 };                                 ///< Indicator的长度
    QRect mDrawIconRect;                                     ///< 记录icon的绘制位置
    QRect mDrawTextRect;                                     ///< 记录text的绘制位置
    QRect mDrawIndicatorArrowRect;                           ///< 记录IndicatorArrow的绘制位置
    QSize mSizeHint;                                         ///< 保存计算好的sizehint
    QSize mLargeButtonSizeHint;                              ///< 大按钮的尺寸
    bool mIsTextNeedWrap { false };                          ///< 标记文字是否需要换行显示
    SARibbonToolButton::LayoutFactor layoutFactor;           ///< 布局系数
    std::unique_ptr< SARibbonToolButtonProxyStyle > mStyle;  ///< 按钮样式，主要为了绘制箭头
};

SARibbonToolButton::PrivateData::PrivateData(SARibbonToolButton* p) : q_ptr(p)
{
    mStyle = std::make_unique< SARibbonToolButtonProxyStyle >();
}

/**
 * @brief 根据鼠标的位置更新状态，主要用于判断鼠标是否位于subcontrol
 *
 * 此函数主要应用在action menu模式下
 * @param pos
 */
void SARibbonToolButton::PrivateData::updateStatusByMousePosition(const QPoint& pos)
{
    bool isMouseOnSubControl(false);
    if (SARibbonToolButton::LargeButton == mButtonType) {
        isMouseOnSubControl = mDrawTextRect.united(mDrawIndicatorArrowRect).contains(pos);
    } else {
        // 小按钮模式就和普通toolbutton一样
        isMouseOnSubControl = mDrawIndicatorArrowRect.contains(pos);
    }

    if (mMouseOnSubControl != isMouseOnSubControl) {
        mMouseOnSubControl = isMouseOnSubControl;
        // 从icon变到text过程中刷新一次
        q_ptr->update();
    }
}

/**
 * @brief 更新绘图的几个关键尺寸
 *
 * 包括：
 *
 * - DrawIconRect 绘制图标的矩形区域
 *
 * - DrawTextRect 绘制文本的矩形区域
 *
 * - DrawIndicatorArrowRect 绘制菜单下箭头的矩形区域
 *
 * @param opt
 */
void SARibbonToolButton::PrivateData::updateDrawRect(const QStyleOptionToolButton& opt)
{
    if (!mSizeHint.isValid()) {
        updateSizeHint(opt);
    }
    // 先更新IndicatorLen
    mIndicatorLen = q_ptr->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, q_ptr);
    if (mIndicatorLen < 3) {
        if (SARibbonToolButton::LargeButton == mButtonType) {
            mIndicatorLen = 8;
        } else {
            mIndicatorLen = 12;  // 小按钮模式下设置为10
        }
    }
    calcDrawRects(opt, mDrawIconRect, mDrawTextRect, mDrawIndicatorArrowRect, mSpacing, mIndicatorLen);
}

/**
 * @brief 更新sizehint
 * @param opt
 */
void SARibbonToolButton::PrivateData::updateSizeHint(const QStyleOptionToolButton& opt)
{
    mSizeHint = calcSizeHint(opt);
}

/**
 * @brief 计算绘图的几个关键区域
 * @param opt
 * @param iconRect  绘制图标的矩形区域
 * @param textRect 绘制文本的矩形区域
 * @param indicatorArrowRect 绘制菜单下箭头的矩形区域
 * @param spacing
 * @param indicatorLen
 */
void SARibbonToolButton::PrivateData::calcDrawRects(const QStyleOptionToolButton& opt,
                                                    QRect& iconRect,
                                                    QRect& textRect,
                                                    QRect& indicatorArrowRect,
                                                    int spacing,
                                                    int indicatorLen) const
{
    if (SARibbonToolButton::LargeButton == mButtonType) {
        calcLargeButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen);

    } else {
        calcSmallButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen);
    }
}

/**
 * @brief 计算小按钮模式下的绘制尺寸
 * @param opt
 * @param iconRect
 * @param textRect
 * @param indicatorArrowRect
 * @param spacing
 * @param indicatorLen
 */
void SARibbonToolButton::PrivateData::calcSmallButtonDrawRects(const QStyleOptionToolButton& opt,
                                                               QRect& iconRect,
                                                               QRect& textRect,
                                                               QRect& indicatorArrowRect,
                                                               int spacing,
                                                               int indicatorLen) const
{
    switch (opt.toolButtonStyle) {
    case Qt::ToolButtonIconOnly: {
        if (hasIndicator(opt)) {
            // 在仅有图标的小模式显示时，预留一个下拉箭头位置
            iconRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing);
            indicatorArrowRect =
                QRect(opt.rect.right() - indicatorLen - spacing, iconRect.y(), indicatorLen, iconRect.height());
        } else {
            iconRect           = opt.rect.adjusted(spacing, spacing, -spacing, -spacing);
            indicatorArrowRect = QRect();
        }
        // 文本区域为空
        textRect = QRect();
    } break;
    case Qt::ToolButtonTextOnly: {
        if (hasIndicator(opt)) {
            // 在仅有图标的小模式显示时，预留一个下拉箭头位置
            textRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing);
            indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, spacing, indicatorLen, textRect.height());
        } else {
            textRect           = opt.rect.adjusted(spacing, spacing, -spacing, -spacing);
            indicatorArrowRect = QRect();
        }
        // 绘图区域为空
        iconRect = QRect();
    } break;
    default: {
        bool hasInd = hasIndicator(opt);
        // icon Beside和under都是一样的
        QRect buttonRect = q_ptr->rect();
        buttonRect.adjust(spacing, spacing, -spacing, -spacing);
        // 先设置IconRect
        if (opt.icon.isNull()) {
            // 没有图标
            iconRect = QRect();
        } else {
            QSize iconSize = adjustIconSize(buttonRect, opt.iconSize);
            iconRect =
                QRect(buttonRect.x(), buttonRect.y(), iconSize.width(), qMax(iconSize.height(), buttonRect.height()));
        }
        // 后设置TextRect
        if (opt.text.isEmpty()) {
            textRect = QRect();
        } else {
            // 分有菜单和没菜单两种情况
            int adjx = iconRect.isValid() ? (iconRect.width() + spacing)
                                          : 0;  // 在buttonRect上变换，因此如果没有图标是不用偏移spacing
            if (hasInd) {
                textRect = buttonRect.adjusted(adjx, 0, -indicatorLen, 0);
            } else {
                textRect = buttonRect.adjusted(adjx, 0, 0, 0);  // 在buttonRect上变换，因此如果没有图标是不用偏移spacing
            }
        }
        // 最后设置Indicator
        if (hasInd) {
            if (textRect.isValid()) {
                indicatorArrowRect =
                    QRect(buttonRect.right() - indicatorLen + 1, textRect.y(), indicatorLen, textRect.height());
            } else if (iconRect.isValid()) {
                indicatorArrowRect =
                    QRect(buttonRect.right() - indicatorLen + 1, iconRect.y(), indicatorLen, iconRect.height());
            } else {
                indicatorArrowRect = buttonRect;
            }
        } else {
            indicatorArrowRect = QRect();
        }
    }
    }
}

/**
 * @brief 计算大按钮模式下的绘制尺寸（普通）
 * @param opt
 * @param iconRect
 * @param textRect
 * @param indicatorArrowRect
 * @param spacing
 * @param indicatorLen
 */
void SARibbonToolButton::PrivateData::calcLargeButtonDrawRects(const QStyleOptionToolButton& opt,
                                                               QRect& iconRect,
                                                               QRect& textRect,
                                                               QRect& indicatorArrowRect,
                                                               int spacing,
                                                               int indicatorLen) const
{
    //! 3行模式的图标比较大，文字换行情况下，indicator会动态调整

    // 初始化
    iconRect           = QRect();
    textRect           = QRect();
    indicatorArrowRect = QRect();

    // 先获取文字矩形的高度
    int textHeight  = calcTextDrawRectHeight(opt);
    bool hIndicator = hasIndicator(opt);
    if (!hIndicator) {
        // 没有菜单，把len设置为0
        indicatorLen = 0;
    }

    // 这里要判断文字是否要换行显示，换行显示的文字的indicatorArrowRect所处的位置不一样
    if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) {
        // 只有图标
        if (hIndicator) {
            // 如果只有icon，且有indicator，那么indicator在图标下面（注意，这个indicator布局和即有图标和文字是不一样的）
            int indicatorHeight = static_cast< int >(indicatorLen * 1.2);
            // 周边留下spacing距离
            indicatorArrowRect = QRect(opt.rect.left() + spacing,
                                       opt.rect.bottom() - indicatorHeight - spacing,
                                       opt.rect.width() - 2 * spacing,
                                       indicatorHeight);
            // iconRect布满整个按钮
            iconRect = QRect(opt.rect.left() + spacing,
                             opt.rect.top() + spacing,
                             opt.rect.width() - 2 * spacing,
                             opt.rect.height() - 2 * spacing - indicatorHeight);
        } else {
            // iconRect布满整个按钮
            iconRect = QRect(opt.rect.left() + spacing,
                             opt.rect.top() + spacing,
                             opt.rect.width() - 2 * spacing,
                             opt.rect.height() - 2 * spacing);
        }
    } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle) {
        // 仅有文字，处理方式和仅有图标一样
        if (hIndicator) {
            // 如果只有text，且有indicator，那么indicator在图标下面（注意，这个indicator布局和即有图标和文字是不一样的）
            int indicatorHeight = static_cast< int >(indicatorLen * 1.2);
            // 周边留下spacing距离
            indicatorArrowRect = QRect(opt.rect.left() + spacing,
                                       opt.rect.bottom() - indicatorHeight - spacing,
                                       opt.rect.width() - 2 * spacing,
                                       indicatorHeight);
            // textRect布满整个按钮
            textRect = QRect(opt.rect.left() + spacing,
                             opt.rect.top() + spacing,
                             opt.rect.width() - 2 * spacing,
                             opt.rect.height() - 2 * spacing - indicatorHeight);
        } else {
            // textRect布满整个按钮
            textRect = QRect(opt.rect.left() + spacing,
                             opt.rect.top() + spacing,
                             opt.rect.width() - 2 * spacing,
                             opt.rect.height() - 2 * spacing);
        }
    } else {
        // 先布置textRect
        if (q_ptr->isEnableWordWrap()) {
            // 在换行模式下
            if (isTextNeedWrap()) {
                // 如果文字的确换行，indicator放在最右边
                textRect = QRect(opt.rect.left() + spacing,
                                 opt.rect.bottom() - spacing - textHeight,
                                 opt.rect.width() - 2 * spacing - indicatorLen,  // 注意，这里会减去indicatorLen的宽度
                                 textHeight);
                if (hIndicator) {
                    // indicator在文字的右边
                    indicatorArrowRect =
                        QRect(textRect.right(), textRect.y() + textRect.height() / 2, indicatorLen, textHeight / 2);
                }
            } else {
                // 如果文字不需要换行，由于文字下面会有一行的空白，因此indicator布局在文字下面
                textRect = QRect(opt.rect.left() + spacing,
                                 opt.rect.bottom() - spacing - textHeight,
                                 opt.rect.width() - 2 * spacing,
                                 textHeight);
                if (hIndicator) {
                    int dy = textRect.height() / 2;
                    dy += (dy - indicatorLen) / 2;
                    indicatorArrowRect = QRect(textRect.left(), textRect.top() + dy, textRect.width(), indicatorLen);
                }
            }
        } else {
            // 文字不换行，indicator放在最右边
            int y = opt.rect.bottom() - spacing - textHeight;
            if (hIndicator) {
                // 先布置indicator
                indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, y, indicatorLen, textHeight);
                textRect           = QRect(spacing, y, indicatorArrowRect.x() - spacing, textHeight);
            } else {
                textRect = QRect(opt.rect.left() + spacing, y, opt.rect.width() - 2 * spacing, textHeight);
            }
        }
        // 剩下就是icon区域
        iconRect = QRect(spacing, spacing, opt.rect.width() - 2 * spacing, textRect.top() - 2 * spacing);
    }
}

/**
 * @brief 适应iconsize
 *
 * 此函数会让originIconSize尽量适配buttonRect的大小
 * @param buttonRect
 * @param originIconSize
 * @return
 */
QSize SARibbonToolButton::PrivateData::adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const
{
    // 边界检查
    if (buttonRect.isEmpty() || originIconSize.isEmpty()) {
        return QSize(0, 0);
    }

    QSize iconSize = originIconSize;

    // 如果图标已经小于等于按钮区域，则直接返回
    if (iconSize.width() <= buttonRect.width() && iconSize.height() <= buttonRect.height()) {
        return iconSize;
    }

    // 计算宽高比
    qreal aspectRatio = static_cast< qreal >(originIconSize.width()) / originIconSize.height();

    // 先按按钮高度调整
    if (iconSize.height() > buttonRect.height()) {
        iconSize.setHeight(buttonRect.height());
        iconSize.setWidth(qRound(buttonRect.height() * aspectRatio));
    }

    // 再检查宽度是否超限
    if (iconSize.width() > buttonRect.width()) {
        iconSize.setWidth(buttonRect.width());
        iconSize.setHeight(qRound(buttonRect.width() / aspectRatio));
    }

    // 确保不会超过按钮边界
    iconSize.setWidth(qMin(iconSize.width(), buttonRect.width()));
    iconSize.setHeight(qMin(iconSize.height(), buttonRect.height()));

    return iconSize;
}

/**
 * @brief 判断是否有Indicator
 * @param opt
 * @return
 */
bool SARibbonToolButton::PrivateData::hasIndicator(const QStyleOptionToolButton& opt) const
{
    return ((opt.features & QStyleOptionToolButton::MenuButtonPopup) || (opt.features & QStyleOptionToolButton::HasMenu));
}

/**
 * @brief 计算sizehint
 *
 * 此函数非常关键，因为所有尺寸计算都是基于原始的rect来的
 * @param opt
 * @return
 */
QSize SARibbonToolButton::PrivateData::calcSizeHint(const QStyleOptionToolButton& opt)
{
    if (SARibbonToolButton::LargeButton == mButtonType) {
        return calcLargeButtonSizeHint(opt);
    }
    return calcSmallButtonSizeHint(opt);
}

QSize SARibbonToolButton::PrivateData::calcSmallButtonSizeHint(const QStyleOptionToolButton& opt)
{
    int w = 0, h = 0;

    switch (opt.toolButtonStyle) {
    case Qt::ToolButtonIconOnly: {
        w = opt.iconSize.width() + 2 * mSpacing;
        h = opt.iconSize.height() + 2 * mSpacing;
    } break;
    case Qt::ToolButtonTextOnly: {
        QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplifiedForRibbonButton(opt.text));
        textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2);
        textSize.setHeight(calcTextDrawRectHeight(opt));
        w = textSize.width() + 2 * mSpacing;
        h = textSize.height() + 2 * mSpacing;
    } break;
    default: {
        // 先加入icon的尺寸
        w = opt.iconSize.width() + 2 * mSpacing;
        h = opt.iconSize.height() + 2 * mSpacing;
        // 再加入文本的长度
        if (!opt.text.isEmpty()) {
            QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplifiedForRibbonButton(opt.text));
            textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2);
            textSize.setHeight(calcTextDrawRectHeight(opt));
            w += mSpacing;
            w += textSize.width();
            h = qMax(h, (textSize.height() + (2 * mSpacing)));
        } else {
            // 没有文本的时候也要设置一下高度
            QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, " ");
            h              = qMax(h, (textSize.height() + (2 * mSpacing)));
        }
    }
    }
    if (hasIndicator(opt)) {
        // 存在indicator的按钮，宽度尺寸要扩展
        w += mIndicatorLen;
    }
    if (w < 16) {
        w = 16;
    }
    //! Qt6.4 取消了QApplication::globalStrut
    return QSize(w, h).expandedTo(QSize(2, 2));
}

QSize SARibbonToolButton::PrivateData::calcLargeButtonSizeHint(const QStyleOptionToolButton& opt)
{
    int w    = 0;
    int h    = opt.fontMetrics.lineSpacing() * 4.8;  // 3*1.6
    int minW = h * 0.75;  // 最小宽度，在panel里面的按钮，最小宽度要和icon适应

    if (SARibbonPanel* panel = qobject_cast< SARibbonPanel* >(q_ptr->parent())) {
        // 对于建立在SARibbonPanel的基础上的大按钮，把高度设置为SARibbonPanel计算的大按钮高度
        h = panel->largeButtonHeight();
    }
    int textHeight = calcTextDrawRectHeight(opt);
    // 估算字体的宽度作为宽度
    w = estimateLargeButtonTextWidth(h, textHeight, opt.text, opt.fontMetrics);
    w += (2 * mSpacing);
    // 判断是否需要加上indicator
    if (q_ptr->isEnableWordWrap() && isTextNeedWrap()) {
        w += mIndicatorLen;
    }

#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT
    qDebug() << "| | |-SARibbonToolButton::PrivateData::calcLargeButtonSizeHint,text=" << opt.text
             << "\n| | | |-lineSpacing*4.5=" << opt.fontMetrics.lineSpacing() * 4.5  //
             << "\n| | | |-textHeight=" << textHeight                                //
             << "\n| | | |-mDrawIconRect=" << mDrawIconRect                          //
             << "\n| | | |-minW=" << minW                                            //
             << "\n| | | |-w=" << w                                                  //
        ;
#endif
    //! Qt6.4 取消了QApplication::globalStrut
    return QSize(w, h).expandedTo(QSize(minW, textHeight));
}

/**
 * @brief 计算文本高度
 * @param opt
 * @return
 */
int SARibbonToolButton::PrivateData::calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const
{
    if (SARibbonToolButton::LargeButton == mButtonType) {
        if (q_ptr->isEnableWordWrap()) {
            return opt.fontMetrics.lineSpacing() * layoutFactor.twoLineHeightFactor + opt.fontMetrics.leading();
        } else {
            return opt.fontMetrics.lineSpacing() * layoutFactor.oneLineHeightFactor;
        }
    }
    // 小按钮
    return opt.rect.height() - 2;
}

/**
 * @brief 估算一个最优的文字尺寸，在可以换行的情况下会进行换行，且只会换一行
 * @param buttonHeight 按钮的高度
 * @param textDrawRectHeight 文本绘制的高度
 * @param fm QFontMetrics
 * @param widthHeightRatio 宽高比，宽度/高度的比值，如果大于这个比值，则会进行尝试换行以获取更低的宽度
 * @param maxTrycount 尝试次数
 * @return
 */
int SARibbonToolButton::PrivateData::estimateLargeButtonTextWidth(int buttonHeight,
                                                                  int textDrawRectHeight,
                                                                  const QString& text,
                                                                  const QFontMetrics& fm,
                                                                  int maxTrycount)
{
    QSize textSize;
    int space        = SA_FONTMETRICS_WIDTH(fm, (QLatin1Char(' '))) * 2;
    int hintMaxWidth = qMin(static_cast< int >(buttonHeight * layoutFactor.buttonMaximumAspectRatio),
                            q_ptr->maximumWidth());  ///< 建议的宽度
    if (q_ptr->isEnableWordWrap()) {
        textSize = fm.size(Qt::TextShowMnemonic, text);
        textSize.setWidth(textSize.width() + space);

        if (textSize.height() > fm.lineSpacing() * 1.1) {
            //! 说明文字带有换行符，是用户手动换行，这种情况就直接返回字体尺寸，不进行估算
            mIsTextNeedWrap = true;  // 文字需要换行显示，标记起来
            return textSize.width();
        }

        // 这时候需要估算文本的长度
        if (textSize.width() <= hintMaxWidth) {
            // 范围合理，直接返回
            mIsTextNeedWrap = false;  // 文字不需要换行显示，标记起来
            return textSize.width();
        }

        //! 大于宽高比尝试进行文字换行
        //! 这里先对文本长度逐渐加长估算，一直到和原来长度一致为止
        int trycount  = 0;
        int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap;
        // 对于英文字体，直接宽度减半是无法满足完全显示两行的，需要进行预估
        QRect textRect(0, 0, textSize.width(), textDrawRectHeight);
        do {
            //! 先计算两行文本的紧凑矩形
            //! 从一半开始逐渐递增
            //! 第1次为 w/2 + w/2 * (0/3)
            //! 第2次为 w/2 + w/2 * (1/3)
            //! 第3次为 w/2 + w/2 * (2/3)
            textRect.setWidth(textSize.width() / 2 + (textSize.width() / 2) * (float(trycount) / maxTrycount));
            textRect = fm.boundingRect(textRect, alignment, text);
            if (textRect.height() <= (fm.lineSpacing() * 2)) {
                // 保证在两行
                mIsTextNeedWrap = true;  // 文字需要换行显示，标记起来
                return textRect.width();
            }
            ++trycount;
#if SARIBBONTOOLBUTTON_DEBUG_DRAW
            if (trycount > 1) {
                qDebug() << "estimateLargeButtonTextWidth,origin textSize=" << textSize << ",trycount=" << trycount
                         << ",textRect=" << textRect;
            }
#endif
        } while (trycount < 3);
        // 到这里说明前面的尝试失败，最终使用原始的长度
        return textSize.width();
    }

    //! 说明是不换行

    mIsTextNeedWrap = false;  // 文字不需要换行显示，标记起来
                              // 文字不换行情况下，做simplified处理
    textSize = fm.size(Qt::TextShowMnemonic, simplifiedForRibbonButton(text));
    textSize.setWidth(textSize.width() + space);
    if (textSize.width() < hintMaxWidth) {
        // 范围合理，直接返回
        return textSize.width();
    }
    return hintMaxWidth;
}

QPixmap SARibbonToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const
{
    if (opt.icon.isNull()) {  // 没有有图标
        return (QPixmap());
    }
    QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off;
    QIcon::Mode mode;
    if (!(opt.state & QStyle::State_Enabled)) {
        mode = QIcon::Disabled;
    } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) {
        mode = QIcon::Active;
    } else {
        mode = QIcon::Normal;
    }
    return SA::iconToPixmap(opt.icon, iconsize, SA::widgetDevicePixelRatio(q_ptr), mode, state);
}

int SARibbonToolButton::PrivateData::getTextAlignment() const
{
    if (q_ptr->toolButtonStyle() == Qt::ToolButtonTextOnly) {
        return Qt::TextShowMnemonic | Qt::AlignCenter;
    }

    if (SARibbonToolButton::LargeButton == mButtonType) {
        return Qt::TextShowMnemonic
               | (q_ptr->isEnableWordWrap() ? (Qt::TextWordWrap | Qt::AlignTop | Qt::AlignHCenter) : Qt::AlignCenter);
    }

    return Qt::TextShowMnemonic | Qt::AlignCenter;
}

/**
 * @brief 确认文字是否确切要换行显示
 * @return
 */
bool SARibbonToolButton::PrivateData::isTextNeedWrap() const
{
    return mIsTextNeedWrap;
}

/**
 * @brief 获取正真的icon尺寸
 * @return
 */
QSize SARibbonToolButton::PrivateData::realIconSize() const
{
    if (mButtonType == SARibbonToolButton::LargeButton) {
        return mLargeButtonSizeHint;
    }
    return q_ptr->smallIconSize();
}

/**
 * @brief 仅仅对\n进行剔除
 * @param str
 * @return
 */
QString SARibbonToolButton::PrivateData::simplifiedForRibbonButton(const QString& str)
{
    QString res = str;
    res.remove('\n');
    return res;
}

//===================================================
// SARibbonToolButton
//===================================================

SARibbonToolButton::SARibbonToolButton(QWidget* parent)
    : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this))
{
    // 静态设置也是可以，虽然节省内存，但不清楚未来qt是否会针对绘制有潜在的多线程处理的可能性，因此这里还是使用成员变量
    // static SARibbonToolButtonProxyStyle* ss_style = new SARibbonToolButtonProxyStyle();
    // setStyle(ss_style);

    // setStyle方法不会接管样式的所有权，因此要手动删除，这里使用智能指针
    setStyle(d_ptr->mStyle.get());
    setAutoRaise(true);
    setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setButtonType(SmallButton);
    setMouseTracking(true);
}

SARibbonToolButton::SARibbonToolButton(QAction* defaultAction, QWidget* parent)
    : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this))
{
    setAutoRaise(true);
    setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setDefaultAction(defaultAction);
    setButtonType(SmallButton);
    setMouseTracking(true);
}

SARibbonToolButton::~SARibbonToolButton()
{
}

/**
 * @brief Sets the layout factor for fine-tuning the button's appearance / 设置布局系数以微调按钮外观
 *
 * This function allows you to customize the button's text height and maximum aspect ratio.
 * After calling this function, the button's geometry will be invalidated to trigger a relayout.
 *
 * 此函数允许您自定义按钮的文本高度和最大宽高比。
 * 调用此函数后，按钮的几何尺寸将被标记为无效，以触发重新布局。
 *
 * Example:
 * @code
 * SARibbonToolButton::LayoutFactor lf;
 * lf.twoLineHeightFactor = 2.2; // Make two-line text taller/让两行文字更高
 * lf.buttonMaximumAspectRatio = 1.6; // Allow a wider button/允许按钮更宽
 * myRibbonButton->setLayoutFactor(lf);
 * @endcode
 *
 * @param fac The new layout factor / 新的布局系数
 * @sa layoutFactor, setButtonMaximumAspectRatio
 */
void SARibbonToolButton::setLayoutFactor(const SARibbonToolButton::LayoutFactor& fac)
{
    d_ptr->layoutFactor = fac;
    // 重新布局
    invalidateSizeHint();
}

/**
 * @brief Gets a const reference to the current layout factor / 获取当前布局系数的常量引用
 * @return A const reference to the layout factor / 布局系数的常量引用
 * @sa setLayoutFactor, setButtonMaximumAspectRatio
 */
const SARibbonToolButton::LayoutFactor& SARibbonToolButton::layoutFactor() const
{
    return d_ptr->layoutFactor;
}

/**
 * @brief Gets a mutable reference to the current layout factor / 获取当前布局系数的可变引用
 * @return A mutable reference to the layout factor / 布局系数的可变引用
 * @sa setLayoutFactor, setButtonMaximumAspectRatio
 */
SARibbonToolButton::LayoutFactor& SARibbonToolButton::layoutFactor()
{
    return d_ptr->layoutFactor;
}

/**
 * @brief Gets the current button type (LargeButton or SmallButton) / 获取当前按钮的类型（大按钮或小按钮）
 * @return The current button type / 当前按钮类型
 * @sa setButtonType
 */
SARibbonToolButton::RibbonButtonType SARibbonToolButton::buttonType() const
{
    return (d_ptr->mButtonType);
}

/**
 * @brief Sets the button type to LargeButton or SmallButton / 设置按钮类型为大按钮或小按钮
 *
 * Changing the button type will invalidate the size hint and trigger a relayout.
 * Note: This function may override the tool button style. If you need to set a specific style (e.g.,
 * Qt::ToolButtonIconOnly), do so after calling this function.
 *
 * 设置按钮类型会令尺寸提示失效并触发重新布局。
 * 注意：此函数可能会覆盖工具按钮样式。如需设置特定样式（例如 Qt::ToolButtonIconOnly），请在此函数调用之后设置。
 *
 * @param buttonType The new button type / 新的按钮类型
 * @sa isLargeRibbonButton, isSmallRibbonButton
 */
void SARibbonToolButton::setButtonType(const RibbonButtonType& buttonType)
{
    d_ptr->mButtonType = buttonType;
    // 计算iconrect
    // 根据字体计算文字的高度

    if (LargeButton == buttonType) {
        setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
    } else {
        setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
    }
    invalidateSizeHint();
}

/**
 * @brief Checks if the button is a small ribbon button / 判断按钮是否为小Ribbon按钮
 * @return `true` if the button type is `SmallButton`; otherwise `false` / 如果按钮类型为 `SmallButton` 则返回 `true`；否则返回 `false`
 * @sa isLargeRibbonButton, buttonType
 */
bool SARibbonToolButton::isSmallRibbonButton() const
{
    return (d_ptr->mButtonType == SmallButton);
}

/**
 * @brief Checks if the button is a large ribbon button / 判断按钮是否为大Ribbon按钮
 * @return `true` if the button type is `LargeButton`; otherwise `false` / 如果按钮类型为 `LargeButton` 则返回 `true`；否则返回 `false`
 * @sa isSmallRibbonButton, buttonType
 */
bool SARibbonToolButton::isLargeRibbonButton() const
{
    return (d_ptr->mButtonType == LargeButton);
}

/**
 * @brief Gets the current spacing value / 获取当前的间距值
 *
 * Spacing is the gap between the icon, text, indicator, and the button's border.
 *
 * 间距是图标、文字、指示器与按钮边框之间的间隙。
 *
 * @return The current spacing in pixels / 当前的间距值（像素）
 * @sa setSpacing
 */
int SARibbonToolButton::spacing() const
{
    return d_ptr->mSpacing;
}

/**
 * @brief Sets the spacing between elements and the border / 设置元素与边框之间的间距
 *
 * This spacing affects the layout of the icon, text, and indicator within the button.
 * After calling this function, the button's geometry will be invalidated to trigger a relayout.
 *
 * 此间距会影响按钮内图标、文字和指示器的布局。
 * 调用此函数后，按钮的几何尺寸将被标记为无效，以触发重新布局。
 *
 * @param v The new spacing value in pixels / 新的间距值（像素）
 * @sa spacing
 */
void SARibbonToolButton::setSpacing(int v)
{
    d_ptr->mSpacing = v;
    invalidateSizeHint();
}

/**
 * @brief Forces an update of the internal layout rectangles / 强制更新内部布局矩形
 *
 * This function recalculates the drawing rectangles for the icon, text, and indicator based on the current button
 * size and style. It also invalidates the cached size hint. This is typically called automatically during a resize
 * event.
 *
 * 此函数会根据当前按钮尺寸和样式，重新计算图标、文字和指示器的绘制矩形。同时会使缓存的尺寸提示失效。
 * 此函数通常在调整大小事件中被自动调用。
 *
 * @note This function invalidates the size hint cache but does not call `updateGeometry()`. If you need to trigger
 * a parent layout update, call `updateGeometry()` manually after this function.
 * / 此函数会清除 sizehint 缓存，但不会调用 updateGeometry()。如果需要触发布局更新，应在调用此函数后手动调用 updateGeometry()。
 */
void SARibbonToolButton::updateRect()
{
    QStyleOptionToolButton opt;
    initStyleOption(&opt);
    d_ptr->updateDrawRect(opt);
    // 这里不调用invalidateSizeHint();因为nvalidateSizeHint();会调用updateGeometry函数，导致父窗口再次布局
    d_ptr->mSizeHint = QSize();
}

/**
 * @brief Enables or disables automatic text wrapping for large buttons / 为大按钮启用或禁用自动文字换行
 *
 * When enabled, the text in a large button will attempt to wrap onto a second line if it is too long to fit on one line.
 * This is particularly useful for long action names in the Ribbon interface.
 * The button's size hint will be recalculated after calling this function.
 *
 * 启用后，如果大按钮中的文字过长无法在一行内显示，将尝试换行到第二行。
 * 这在Ribbon界面中处理较长的操作名称时非常有用。
 * 调用此函数后，按钮的size hint将被重新计算。
 *
 * Example:
 * @code
 * // Enable word wrap for a button with a potentially long label/为一个可能有长标签的按钮启用文字换行
 * myLongLabelButton->setEnableWordWrap(true);
 * @endcode
 *
 * @param on `true` to enable word wrap; `false` to disable it / `true` 启用换行，`false` 禁用换行
 * @sa isEnableWordWrap
 */
void SARibbonToolButton::setEnableWordWrap(bool on)
{
    d_ptr->mWordWrap = on;
    // 通知父布局需要重新布局
    invalidateSizeHint();
}

/**
 * @brief Checks if automatic text wrapping is enabled / 检查是否启用了自动文字换行
 * @return `true` if word wrap is enabled; otherwise `false` / 如果启用了文字换行则返回 `true`；否则返回 `false`
 * @sa setEnableWordWrap
 */
bool SARibbonToolButton::isEnableWordWrap()
{
    return d_ptr->mWordWrap;
}

/**
 * @brief Sets the button's maximum aspect ratio (width/height) / 设置按钮的最大宽高比
 *
 * This is a convenience function that directly sets the `buttonMaximumAspectRatio` member of the `LayoutFactor`
 * structure. It has the same effect as modifying the structure and calling `setLayoutFactor`.
 *
 * 此函数是直接设置 `LayoutFactor` 结构体中 `buttonMaximumAspectRatio` 成员的便捷方法。
 * 其效果等同于修改结构体后调用 `setLayoutFactor`。
 *
 * @param v The new maximum aspect ratio value / 新的最大宽高比值
 * @sa buttonMaximumAspectRatio, setLayoutFactor
 */
void SARibbonToolButton::setButtonMaximumAspectRatio(qreal v)
{
    d_ptr->layoutFactor.buttonMaximumAspectRatio = v;
    // 重新布局
    invalidateSizeHint();
}

/**
 * @brief Gets the button's maximum aspect ratio (width/height) / 获取按钮的最大宽高比
 * @return The current maximum aspect ratio / 当前的最大宽高比
 * @sa setButtonMaximumAspectRatio, layoutFactor
 */
qreal SARibbonToolButton::buttonMaximumAspectRatio() const
{
    return layoutFactor().buttonMaximumAspectRatio;
}

bool SARibbonToolButton::event(QEvent* e)
{
    switch (e->type()) {
    case QEvent::WindowDeactivate:
        d_ptr->mMouseOnSubControl = false;
        break;
    case QEvent::ActionChanged:
    case QEvent::ActionRemoved:
    case QEvent::ActionAdded: {
        d_ptr->mMouseOnSubControl = false;
        invalidateSizeHint();
    } break;
    default:
        break;
    }

    return (QToolButton::event(e));
}

void SARibbonToolButton::changeEvent(QEvent* e)
{
    if (e) {
        switch (e->type()) {
        case QEvent::FontChange:
        case QEvent::StyleChange:
        case QEvent::LanguageChange: {
            // 说明字体改变，需要重新计算和字体相关的信息
            invalidateSizeHint();
        } break;
        case QEvent::ScreenChangeInternal: {
            invalidateSizeHint();
        }
        default:
            break;
        }
    }
    QToolButton::changeEvent(e);
}

/**
 * @brief 鼠标移动事件
 *
 * 由于Ribbon的Indicator和正常的Toolbutton不一样，因此无法用QStyleOptionToolButton的activeSubControls的状态
 *
 * 因此需要重新捕获鼠标的位置来更新按钮当前的一些状态
 * @param e
 */
void SARibbonToolButton::mouseMoveEvent(QMouseEvent* e)
{
    d_ptr->updateStatusByMousePosition(e->pos());
    QToolButton::mouseMoveEvent(e);
}

/**
 * @brief SARibbonToolButton::mousePressEvent
 * @param e
 */
void SARibbonToolButton::mousePressEvent(QMouseEvent* e)
{
    if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) {
        d_ptr->updateStatusByMousePosition(e->pos());
        if (d_ptr->mMouseOnSubControl) {
            d_ptr->mMenuButtonPressed = true;
            showMenu();
            // showmenu结束后，在判断当前的鼠标位置是否是在subcontrol
            d_ptr->updateStatusByMousePosition(mapFromGlobal(QCursor::pos()));
            return;
        }
    }
    d_ptr->mMenuButtonPressed = false;
    //! 注意这里要用QAbstractButton的mousePressEvent，而不是QToolButton的mousePressEvent
    //! QToolButton的mousePressEvent主要是为了弹出菜单，这里弹出菜单的方式是不一样的，因此不能执行QToolButton的mousePressEvent
    QToolButton::mousePressEvent(e);
}

void SARibbonToolButton::mouseReleaseEvent(QMouseEvent* e)
{
    d_ptr->mMenuButtonPressed = false;
    QToolButton::mouseReleaseEvent(e);
}

void SARibbonToolButton::focusOutEvent(QFocusEvent* e)
{
    d_ptr->mMouseOnSubControl = false;
    QToolButton::focusOutEvent(e);
}

void SARibbonToolButton::leaveEvent(QEvent* e)
{
    d_ptr->mMouseOnSubControl = false;
    QToolButton::leaveEvent(e);
}

bool SARibbonToolButton::hitButton(const QPoint& pos) const
{
    if (QToolButton::hitButton(pos)) {
        return (!d_ptr->mMenuButtonPressed);
    }
    return (false);
}

/**
 * @brief 在resizeevent计算绘图所需的尺寸，避免在绘图过程中实时绘制提高效率
 * @param e
 */
void SARibbonToolButton::resizeEvent(QResizeEvent* e)
{
    // 在resizeevent计算绘图所需的尺寸，避免在绘图过程中实时绘制提高效率
    QToolButton::resizeEvent(e);
    updateRect();
}

/**
 * @brief Returns the recommended size for the button / 返回按钮的推荐尺寸
 *
 * This size is calculated based on the button's type, text, icon, and current layout factors.
 * The result is cached for performance. The cache is invalidated when relevant properties change.
 *
 * 此尺寸是根据按钮的类型、文字、图标和当前布局系数计算得出的。
 * 为提高性能，计算结果会被缓存。当相关属性改变时，缓存会自动失效。
 *
 * @return The recommended size / 推荐的尺寸
 */
QSize SARibbonToolButton::sizeHint() const
{
#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT
    qDebug() << "| | |-SARibbonToolButton::sizeHint";
#endif
    if (d_ptr->mSizeHint.isValid()) {
        return d_ptr->mSizeHint;
    }
    QStyleOptionToolButton opt;
    initStyleOption(&opt);
    d_ptr->updateSizeHint(opt);
    return d_ptr->mSizeHint;
}

/**
 * @brief Returns the recommended minimum size for the button / 返回按钮的推荐最小尺寸
 *
 * For `SARibbonToolButton`, the minimum size hint is the same as the size hint.
 *
 * 对于 `SARibbonToolButton`，最小尺寸提示与尺寸提示相同。
 *
 * @return The recommended minimum size / 推荐的最小尺寸
 */
QSize SARibbonToolButton::minimumSizeHint() const
{
    return (sizeHint());
}

void SARibbonToolButton::actionEvent(QActionEvent* e)
{
    QToolButton::actionEvent(e);
    invalidateSizeHint();
}

void SARibbonToolButton::paintEvent(QPaintEvent* e)
{
    Q_UNUSED(e);
    QPainter p(this);
    QStyleOptionToolButton opt;
    initStyleOption(&opt);
    if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) {
        // 在菜单弹出消失后，需要通过此方法取消掉鼠标停留
        if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
            opt.state &= ~QStyle::State_MouseOver;
        }
    }
    paintButton(p, opt);
    paintIcon(p, opt, d_ptr->mDrawIconRect);
    paintText(p, opt, d_ptr->mDrawTextRect);
    paintIndicator(p, opt, d_ptr->mDrawIndicatorArrowRect);
}

/**
 * @brief Paints the button's background and frame / 绘制按钮的背景和边框
 *
 * This function handles the special visual effects for the Ribbon style, particularly for the `MenuButtonPopup`
 * mode where the icon and text areas can have different hover states.
 *
 * 此函数处理Ribbon样式的特殊视觉效果，特别是在 `MenuButtonPopup` 模式下，图标和文字区域可以有不同的悬停状态。
 *
 * @param p The painter to use / 用于绘制的painter
 * @param opt The style option for the tool button / 工具按钮的样式选项
 */
void SARibbonToolButton::paintButton(QPainter& p, const QStyleOptionToolButton& opt)
{
    // QStyle::State_Sunken 代表按钮按下去了
    // QStyle::State_On 代表按钮按checked
    // QStyle::State_MouseOver 代表当前鼠标位于按钮上面
    QStyleOption tool = opt;
    bool autoRaise    = opt.state & QStyle::State_AutoRaise;
    // 绘制按钮
    if (autoRaise) {
        // 这个是为了实现按钮点击下去后(QStyle::State_Sunken),能出现选中的状态
        // 先绘制一个鼠标不在按钮上的状态
        if (opt.state & QStyle::State_Sunken) {
            tool.state &= ~QStyle::State_MouseOver;
        }
        style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this);
    } else {
        style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this);
    }
    // 针对MenuButtonPopup的ribbon样式的特殊绘制
    if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) {
        if (opt.state & QStyle::State_MouseOver) {                       // 鼠标在按钮上才进行绘制
            if (!(opt.activeSubControls & QStyle::SC_ToolButtonMenu)) {  // 按钮的菜单弹出时不做处理
                if (LargeButton == d_ptr->mButtonType) {                 // 大按钮模式
                    if (d_ptr->mMouseOnSubControl) {                     // 此时鼠标在indecater那
                        // 鼠标在文字区，把图标显示为正常（就是鼠标不放上去的状态）
                        tool.rect = d_ptr->mDrawIconRect;
                        tool.state |= (QStyle::State_Raised);  // 把图标区域显示为正常
                        tool.state &= ~QStyle::State_MouseOver;
                        if (autoRaise) {
                            style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this);
                        } else {
                            style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this);
                        }
                    } else {
                        // 鼠标在图标区，把文字显示为正常
                        if (!tool.state.testFlag(QStyle::State_Sunken)) {
                            // State_Sunken说明此按钮正在按下，这时候，文本区域不需要绘制，只有在非按下状态才需要绘制
                            tool.state |= (QStyle::State_Raised);  // 把图标区域显示为正常
                            tool.state &= ~QStyle::State_MouseOver;
                            // 文字和Indicator都显示正常
                            tool.rect = d_ptr->mDrawTextRect.united(d_ptr->mDrawIndicatorArrowRect);
                            if (autoRaise) {
                                style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this);
                            } else {
                                style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this);
                            }
                        }
                    }
                } else {                              // 小按钮模式
                    if (d_ptr->mMouseOnSubControl) {  // 此时鼠标在indecater那
                        // 鼠标在文字区，把图标和文字显示为正常
                        tool.rect  = d_ptr->mDrawIconRect.united(d_ptr->mDrawTextRect);
                        tool.state = (QStyle::State_Raised);  // 把图标区域显示为正常
                        if (autoRaise) {
                            style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this);
                        } else {
                            style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this);
                        }
                    } else {
                        // 鼠标在图标区，把文字显示为正常
                        tool.state = (QStyle::State_Raised);  // 把图标区域显示为正常
                        // 文字和Indicator都显示正常
                        tool.rect = d_ptr->mDrawIndicatorArrowRect;
                        if (autoRaise) {
                            style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this);
                        } else {
                            style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this);
                        }
                    }
                }
            }
        }
    }
    // 绘制Focus
    //     if (opt.state & QStyle::State_HasFocus) {
    //         QStyleOptionFocusRect fr;
    //         fr.QStyleOption::operator=(opt);
    //         fr.rect.adjust(d_ptr->mSpacing, d_ptr->mSpacing, -d_ptr->mSpacing, -d_ptr->mSpacing);
    //         style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, &p, this);
    //     }
}

/**
 * @brief Paints the button's icon / 绘制按钮的图标
 *
 * The icon is painted within the specified rectangle, scaled appropriately based on the available space.
 *
 * 图标会在指定的矩形区域内绘制，并根据可用空间进行适当缩放。
 *
 * @param p The painter to use / 用于绘制的painter
 * @param opt The style option for the tool button / 工具按钮的样式选项
 * @param iconDrawRect The rectangle in which to draw the icon / 绘制图标的矩形区域
 */
void SARibbonToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect)
{
    if (!iconDrawRect.isValid()) {
        return;
    }

    QPixmap pm = d_ptr->createIconPixmap(opt, d_ptr->realIconSize());
    style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm);
    SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, iconDrawRect);
}

/**
 * @brief Paints the button's text / 绘制按钮的文字
 *
 * The text is painted within the specified rectangle, with alignment and elision (truncation with "...") handled
 * according to the button's type and word-wrap setting.
 *
 * 文字会在指定的矩形区域内绘制，其对齐方式和省略（用“...”截断）会根据按钮的类型和文字换行设置进行处理。
 *
 * @param p The painter to use / 用于绘制的painter对象
 * @param opt The style option for the tool button / 工具按钮的样式选项
 * @param textDrawRect The rectangle in which to draw the text / 绘制文字的矩形区域
 */
void SARibbonToolButton::paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect)
{
    int alignment = d_ptr->getTextAlignment();

    if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) {
        alignment |= Qt::TextHideMnemonic;
    }
    QString text;
    if (isSmallRibbonButton()) {
        text = opt.fontMetrics.elidedText(
            PrivateData::simplifiedForRibbonButton(opt.text), Qt::ElideRight, textDrawRect.width(), alignment);
    } else {
        if (!isEnableWordWrap()) {
            text = opt.fontMetrics.elidedText(
                PrivateData::simplifiedForRibbonButton(opt.text), Qt::ElideRight, textDrawRect.width(), alignment);
        } else {
            text = opt.text;
        }
    }
    //! 以下内容参考QCommonStyle.cpp
    //! void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,QPainter *p, const QWidget *widget) const
    //! case CC_ToolButton:
    QStyle::State bflags = opt.state & ~QStyle::State_Sunken;
    if (bflags & QStyle::State_AutoRaise) {
        if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) {
            bflags &= ~QStyle::State_Raised;
        }
    }
    if (opt.state & QStyle::State_Sunken) {
        if (opt.activeSubControls & QStyle::SC_ToolButton) {
            bflags |= QStyle::State_Sunken;
        }
    }
    QStyleOptionToolButton label = opt;
    label.state                  = bflags;
    style()->drawItemText(
        &p, textDrawRect, alignment, label.palette, label.state & QStyle::State_Enabled, text, QPalette::ButtonText);
    SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, textDrawRect);
}

/**
 * @brief Paints the button's indicator (e.g., dropdown arrow) / 绘制按钮的指示器（例如下拉箭头）
 *
 * The indicator is painted within the specified rectangle if the button has a menu (i.e., features include
 * `MenuButtonPopup` or `HasMenu`).
 *
 * 如果按钮有菜单（即特性包含 `MenuButtonPopup` 或 `HasMenu`），则会在指定的矩形区域内绘制指示器。
 *
 * @param p The painter to use / 用于绘制的painter对象
 * @param opt The style option for the tool button / 工具按钮的样式选项
 * @param indicatorDrawRect The rectangle in which to draw the indicator / 绘制指示器的矩形区域
 */
void SARibbonToolButton::paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect)
{
    if (!indicatorDrawRect.isValid() || !d_ptr->hasIndicator(opt)) {
        return;
    }

    QStyleOption tool = opt;
    tool.rect         = indicatorDrawRect;
    style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, this);
    SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, indicatorDrawRect);
}

/**
 * @brief Invalidates the cached size hint / 使缓存的size hint失效
 *
 * This function clears the internally cached `sizeHint()` value and calls `updateGeometry()`,
 * which notifies the layout system that this widget needs to be relayouted.
 * It is called automatically when properties affecting the size (like text, font, or button type) change.
 *
 * 此函数会清除内部缓存的 `sizeHint()` 值并调用 `updateGeometry()`，
 * 通知布局系统此控件需要重新布局。
 * 当影响尺寸的属性（如文字、字体或按钮类型）发生变化时，会自动调用此函数。
 */
void SARibbonToolButton::invalidateSizeHint()
{
    d_ptr->mSizeHint = QSize();
    updateGeometry();
}

/**
 * @brief 大按钮的尺寸
 * @param largeSize
 */
void SARibbonToolButton::setLargeIconSize(const QSize& largeSize)
{
    d_ptr->mLargeButtonSizeHint = largeSize;
}

/**
 * @brief 大按钮的尺寸
 * @return
 */
QSize SARibbonToolButton::largeIconSize() const
{
    return d_ptr->mLargeButtonSizeHint;
}

/**
 * @brief 小按钮尺寸
 * @param smallSize
 */
void SARibbonToolButton::setSmallIconSize(const QSize& smallSize)
{
    setIconSize(smallSize);
}

/**
 * @brief 小按钮尺寸
 * @return
 */
QSize SARibbonToolButton::smallIconSize() const
{
    return iconSize();
}

void SARibbonToolButton::drawArrow(const QStyle* style,
                                   const QStyleOptionToolButton* toolbutton,
                                   const QRect& rect,
                                   QPainter* painter,
                                   const QWidget* widget)
{
    QStyle::PrimitiveElement pe;

    switch (toolbutton->arrowType) {
    case Qt::LeftArrow:
        pe = QStyle::PE_IndicatorArrowLeft;
        break;

    case Qt::RightArrow:
        pe = QStyle::PE_IndicatorArrowRight;
        break;

    case Qt::UpArrow:
        pe = QStyle::PE_IndicatorArrowUp;
        break;

    case Qt::DownArrow:
        pe = QStyle::PE_IndicatorArrowDown;
        break;

    default:
        return;
    }
    QStyleOption arrowOpt = *toolbutton;

    arrowOpt.rect = rect;
    style->drawPrimitive(pe, &arrowOpt, painter, widget);
}

/*** End of inlined file: SARibbonToolButton.cpp ***/

/*** Start of inlined file: SARibbonColorToolButton.cpp ***/
#include <QStylePainter>
#include <QStyleOptionToolButton>
#include <QDebug>
#include <QApplication>
#include <QScreen>

//===================================================
// SARibbonColorToolButton::PrivateData
//===================================================
const int c_ribbonbutton_color_height = 5;  ///< 颜色块的高度

class SARibbonColorToolButton::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonColorToolButton)
public:
    PrivateData(SARibbonColorToolButton* p);
    QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const;
    QIcon createColorIcon(const QColor& c, const QSize& size) const;

public:
    QColor mColor;                                                                                ///< 记录颜色
    SARibbonColorToolButton::ColorStyle mColorStyle { SARibbonColorToolButton::ColorUnderIcon };  ///< 颜色显示样式
    QIcon mOldIcon;                                                                               ///< 记录旧的icon
};

SARibbonColorToolButton::PrivateData::PrivateData(SARibbonColorToolButton* p) : q_ptr(p)
{
}

QPixmap SARibbonColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const
{
    if (opt.icon.isNull()) {  // 没有有图标
        return QPixmap();
    }
    // 有icon，在icon下方加入颜色
    QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off;
    QIcon::Mode mode;
    if (!(opt.state & QStyle::State_Enabled)) {
        mode = QIcon::Disabled;
    } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) {
        mode = QIcon::Active;
    } else {
        mode = QIcon::Normal;
    }
    QSize realIconSize = iconsize - QSize(0, c_ribbonbutton_color_height + 1);
    QPixmap pixmap     = SA::iconToPixmap(opt.icon, realIconSize, SA::widgetDevicePixelRatio(q_ptr), mode, state);
    // QPixmap pixmap     = opt.icon.pixmap(q_ptr->window()->windowHandle(), realIconSize, mode, state);
    QPixmap res(pixmap.size() + QSize(4, c_ribbonbutton_color_height + 4));  // 宽度上，颜色块多出2px
    res.fill(Qt::transparent);
    QPainter painter(&res);
    int xpixmap = (res.width() - pixmap.width()) / 2;
    int ypixmap = (res.height() - c_ribbonbutton_color_height - 2 - pixmap.height())
                  / 2;  // 这里要减去2而不是1，这样奇数偶数都不会影响
    int w         = pixmap.width();
    int h         = pixmap.height();
    QRect rpixmap = QRect(xpixmap, ypixmap, w, h);
    painter.drawPixmap(rpixmap, pixmap);
    QRect colorRect = rpixmap.adjusted(0, h + 1, 0, c_ribbonbutton_color_height + 1);
    if (mColor.isValid()) {
        painter.fillRect(colorRect, mColor);
    } else {
        QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap);
        painter.setPen(pen);
        painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
        painter.setRenderHint(QPainter::Antialiasing, true);
        int ss = colorRect.width() / 3;
        painter.drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top()));
        pen.setColor(Qt::black);
        painter.setPen(pen);
        painter.drawRect(colorRect);
    }
    return res;
}

QIcon SARibbonColorToolButton::PrivateData::createColorIcon(const QColor& c, const QSize& size) const
{
    QPixmap res(size);
    res.fill(Qt::transparent);
    QPainter painter(&res);
    if (c.isValid()) {
        painter.fillRect(QRect(1, 1, res.height() - 2, res.width() - 2), c);
    } else {
        QPen pen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap);
        painter.setPen(pen);
        painter.drawRect(QRect(1, 1, res.height() - 2, res.width() - 2));
        pen.setColor(Qt::red);
        painter.setPen(pen);
        painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.drawLine(QPoint(1, size.height()), QPoint(size.width() - 1, 1));
    }
    return QIcon(res);
}

//===================================================
// SARibbonColorToolButton
//===================================================

SARibbonColorToolButton::SARibbonColorToolButton(QWidget* parent)
    : SARibbonToolButton(parent), d_ptr(new SARibbonColorToolButton::PrivateData(this))
{
    connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked);
}

SARibbonColorToolButton::SARibbonColorToolButton(QAction* defaultAction, QWidget* parent)
    : SARibbonToolButton(defaultAction, parent), d_ptr(new SARibbonColorToolButton::PrivateData(this))
{
    connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked);
}

SARibbonColorToolButton::~SARibbonColorToolButton()
{
}

/**
 * @brief 获取按钮维护的颜色
 * @return
 */
QColor SARibbonColorToolButton::color() const
{
    return d_ptr->mColor;
}

/**
 * @brief 设置颜色显示的样式
 * @param s
 */
void SARibbonColorToolButton::setColorStyle(SARibbonColorToolButton::ColorStyle s)
{
    if (d_ptr->mColorStyle == s) {
        return;
    }
    d_ptr->mColorStyle = s;
    if (ColorUnderIcon == s) {
        setIcon(d_ptr->mOldIcon);
    } else {
        d_ptr->mOldIcon = icon();
        setIcon(d_ptr->createColorIcon(d_ptr->mColor, QSize(32, 32)));
    }
    repaint();
}

/**
 * @brief 颜色显示的样式
 * @return
 */
SARibbonColorToolButton::ColorStyle SARibbonColorToolButton::colorStyle() const
{
    return d_ptr->mColorStyle;
}

/**
 * @brief 建立标准的颜色菜单
 * @return
 */
SAColorMenu* SARibbonColorToolButton::setupStandardColorMenu()
{
    setPopupMode(QToolButton::MenuButtonPopup);
    SAColorMenu* m = new SAColorMenu(this);
    m->enableNoneColorAction(true);
    QAction* customColor = m->customColorAction();
    if (customColor) {
        customColor->setIcon(QIcon(":/SARibbon/image/resource/define-color.svg"));
    }
    connect(m, &SAColorMenu::selectedColor, this, &SARibbonColorToolButton::setColor);
    setMenu(m);

    updateRect();
    return m;
}

/**
 * @brief 设置按钮的颜色
 *
 * 此时会生成一个新的icon
 * @note 会发射@sa colorChanged 信号
 * @param c
 */
void SARibbonColorToolButton::setColor(const QColor& c)
{
    if (d_ptr->mColor != c) {
        d_ptr->mColor = c;
        if (ColorFillToIcon == colorStyle()) {
            setIcon(d_ptr->createColorIcon(c, QSize(32, 32)));
        }
        repaint();
        Q_EMIT colorChanged(c);
    }
}

void SARibbonColorToolButton::onButtonClicked(bool checked)
{
    Q_EMIT colorClicked(d_ptr->mColor, checked);
}

/**
 * @brief 重写paintIcon函数，把颜色加到icon下面
 * @param p
 * @param opt
 * @param iconDrawRect
 */
void SARibbonColorToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect)
{
    if (ColorUnderIcon == colorStyle()) {
        // 有icon
        QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size());
        style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm);
    } else {
        SARibbonToolButton::paintIcon(p, opt, iconDrawRect);
    }
}

/*** End of inlined file: SARibbonColorToolButton.cpp ***/

/*** Start of inlined file: SARibbonLineWidgetContainer.cpp ***/
#include <QHBoxLayout>

SARibbonLineWidgetContainer::SARibbonLineWidgetContainer(QWidget* par) : QWidget(par), m_innerWidget(nullptr)
{
    m_labelPrefix    = new QLabel(this);
    m_labelSuffix    = new QLabel(this);
    QHBoxLayout* lay = new QHBoxLayout();

    lay->setContentsMargins(0, 0, 0, 0);
    lay->setSpacing(0);
    lay->addWidget(m_labelPrefix);
    lay->addWidget(m_labelSuffix);
    setLayout(lay);
}

SARibbonLineWidgetContainer::~SARibbonLineWidgetContainer()
{
}

void SARibbonLineWidgetContainer::setWidget(QWidget* innerWidget)
{
    QHBoxLayout* lay = static_cast< QHBoxLayout* >(layout());

    if (m_innerWidget) {
        lay->replaceWidget(m_innerWidget, innerWidget);
    } else {
        lay->insertWidget(1, innerWidget);
    }
    m_innerWidget = innerWidget;
}

void SARibbonLineWidgetContainer::setPrefix(const QString& str)
{
    m_labelPrefix->setText(str);
}

void SARibbonLineWidgetContainer::setSuffix(const QString& str)
{
    m_labelSuffix->setText(str);
}

QLabel* SARibbonLineWidgetContainer::labelPrefix() const
{
    return (m_labelPrefix);
}

QLabel* SARibbonLineWidgetContainer::labelSuffix() const
{
    return (m_labelSuffix);
}

/*** End of inlined file: SARibbonLineWidgetContainer.cpp ***/

/*** Start of inlined file: SARibbonActionsManager.cpp ***/
#include <QMap>
#include <QHash>
#include <QDebug>
#include <QWidgetAction>

class SARibbonActionsManager::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager)
public:
    PrivateData(SARibbonActionsManager* p);
    void clear();

    QMap< int, QList< QAction* > > mTagToActions;   ///< tag : QList<QAction*>
    QMap< int, QString > mTagToName;                ///< tag对应的名字
    QHash< QString, QAction* > mKeyToAction;        ///< key对应action
    QMap< QAction*, QString > mActionToKey;         ///< action对应key
    QMap< int, SARibbonCategory* > mTagToCategory;  ///< 仅仅在autoRegisteActions函数会有用
    int mSale;  ///< 盐用于生成固定的id，在用户不主动设置key时，id基于msale生成，只要SARibbonActionsManager的调用registeAction顺序不变，生成的id都不变，因为它是基于自增实现的
};

SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0)
{
}

void SARibbonActionsManager::PrivateData::clear()
{
    mTagToActions.clear();
    mTagToName.clear();
    mKeyToAction.clear();
    mActionToKey.clear();
    mTagToCategory.clear();
    mSale = 0;
}

SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar)
    : QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this))
{
    autoRegisteActions(bar);
}

SARibbonActionsManager::~SARibbonActionsManager()
{
}

/**
 * @brief 设置tag对应的名字，通过这个可以得到tag和文本的映射
 * @param tag
 * @param name
 * @note 在支持多语言的环境下，在语言切换时需要重新设置，以更新名字
 */
void SARibbonActionsManager::setTagName(int tag, const QString& name)
{
    d_ptr->mTagToName[ tag ] = name;
}

/**
 * @brief 获取tag对应的中文名字
 * @param tag
 * @return
 */
QString SARibbonActionsManager::tagName(int tag) const
{
    return (d_ptr->mTagToName.value(tag, ""));
}

/**
 * @brief 移除tag
 * @note 注意，这个函数非常耗时
 * @param tag
 */
void SARibbonActionsManager::removeTag(int tag)
{
    QList< QAction* > oldacts = actions(tag);

    // 开始移除
    d_ptr->mTagToActions.remove(tag);
    d_ptr->mTagToName.remove(tag);
    // 开始查找需要移出总表的action
    QList< QAction* > needRemoveAct;
    QList< QAction* > total;

    for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) {
        total += i.value();
    }
    for (QAction* a : qAsConst(oldacts)) {
        if (!total.contains(a)) {
            needRemoveAct.append(a);
        }
    }
    // 从总表移除action
    for (QAction* a : qAsConst(needRemoveAct)) {
        auto i = d_ptr->mActionToKey.find(a);
        if (i != d_ptr->mActionToKey.end()) {
            d_ptr->mKeyToAction.remove(i.value());
            d_ptr->mActionToKey.erase(i);
        }
    }
}

/**
 * @brief 把action注册到管理器中，实现action的管理
 * @param act
 * @param tag tag是可以按照位进行叠加，见 @ref ActionTag 如果
 * 要定义自己的标签，建议定义大于@ref ActionTag::UserDefineActionTag 的值，
 * registeAction的tag是直接记录进去的，如果要多个标签并存，在registe之前先或好tag
 * @param key key是action对应的key，一个key只对应一个action，是查找action的关键
 * ,默认情况为一个QString(),这时key是QAction的objectName
 * @param enableEmit 控制是否发射@ref actionTagChanged 信号
 * @note 同一个action多次注册不同的tag可以通过tag索引到action，但通过action只能索引到最后一个注册的tag
 * @note tag的新增会触发actionTagChanged信号
 */
bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit)
{
    if (nullptr == act) {
        return (false);
    }
    QString k = key;

    if (k.isEmpty()) {
        k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName());
    }
    if (d_ptr->mKeyToAction.contains(k)) {
        qWarning() << "key: "
                   << k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction";
        return (false);
    }
    d_ptr->mKeyToAction[ k ]   = act;
    d_ptr->mActionToKey[ act ] = k;
    // 记录tag 对 action
    bool isneedemit = !(d_ptr->mTagToActions.contains(tag));  // 记录是否需要发射信号
    d_ptr->mTagToActions[ tag ].append(act);
    // 绑定槽
    connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed);
    if (isneedemit && enableEmit) {
        // 说明新增tag
        Q_EMIT actionTagChanged(tag, false);
    }
    return (true);
}

/**
 * @brief 取消action的注册
 *
 * 如果tag对应的最后一个action被撤销，tag也将一块删除
 * @param act
 * @note tag的删除会触发actionTagChanged信号
 * @note 如果action关联了多个tag，这些tag里的action都会被删除，对应的key也同理
 */
void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit)
{
    if (nullptr == act) {
        return;
    }
    // 绑定槽
    disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed);
    removeAction(act, enableEmit);
}

/**
 * @brief 移除action
 *
 * 仅移除内存内容
 * @param act
 * @param enableEmit
 */
void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit)
{
    QList< int > deletedTags;                     // 记录删除的tag，用于触发actionTagChanged
    QMap< int, QList< QAction* > > tagToActions;  ///< tag : QList<QAction*>

    for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) {
        // 把不是act的内容转移到tagToActions和tagToActionKeys中，之后再和m_d里的替换
        auto tmpi = tagToActions.insert(i.key(), QList< QAction* >());
        int count = 0;
        for (int j = 0; j < i.value().size(); ++j) {
            if (i.value()[ j ] != act) {
                tmpi.value().append(act);
                ++count;
            }
        }
        if (0 == count) {
            // 说明这个tag没有内容
            tagToActions.erase(tmpi);
            deletedTags.append(i.key());
        }
    }
    // 删除mKeyToAction
    QString key = d_ptr->mActionToKey.value(act);

    d_ptr->mActionToKey.remove(act);
    d_ptr->mKeyToAction.remove(key);

    // 置换
    d_ptr->mTagToActions.swap(tagToActions);
    // 发射信号
    if (enableEmit) {
        for (int tagdelete : qAsConst(deletedTags)) {
            Q_EMIT actionTagChanged(tagdelete, true);
        }
    }
}

/**
 * @brief 等同actions
 * @param tag
 * @return
 */
QList< QAction* >& SARibbonActionsManager::filter(int tag)
{
    return (actions(tag));
}

/**
 * @brief 根据tag得到actions
 * @param tag
 * @return
 */
QList< QAction* >& SARibbonActionsManager::actions(int tag)
{
    return (d_ptr->mTagToActions[ tag ]);
}

const QList< QAction* > SARibbonActionsManager::actions(int tag) const
{
    return (d_ptr->mTagToActions[ tag ]);
}

/**
 * @brief 获取所有的标签
 * @return
 */
QList< int > SARibbonActionsManager::actionTags() const
{
    return (d_ptr->mTagToActions.keys());
}

/**
 * @brief 通过key获取action
 * @param key
 * @return 如果没有key，返回nullptr
 */
QAction* SARibbonActionsManager::action(const QString& key) const
{
    return (d_ptr->mKeyToAction.value(key, nullptr));
}

/**
 * @brief 通过action找到key
 * @param act
 * @return 如果找不到，返回QString()
 */
QString SARibbonActionsManager::key(QAction* act) const
{
    return (d_ptr->mActionToKey.value(act, QString()));
}

/**
 * @brief 返回所有管理的action数
 * @return
 */
int SARibbonActionsManager::count() const
{
    return (d_ptr->mKeyToAction.size());
}

/**
 * @brief 返回所有管理的actions
 * @return
 */
QList< QAction* > SARibbonActionsManager::allActions() const
{
    return (d_ptr->mKeyToAction.values());
}

/**
 * @brief 自动加载SARibbonBar的action
 * 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object，找到action注册，
 * 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类
 *
 * 此函数会把所有category下的action生成tag并注册，返回的QMap<int, SARibbonCategory *>是记录了category对应的tag
 *
 * 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action，但不在任何一个category下的作为NotInRibbonCategoryTag标签注册，默认名字会赋予not
 * in ribbon， 可以通过@ref setTagName 改变
 *
 * @param w
 * @return
 * @note 此函数的调用最好在category设置了标题后调用，因为会以category的标题作为标签的命名
 */
QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar)
{
    QMap< int, SARibbonCategory* > res;
    // 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象，把所有action找到
    QWidget* parWidget = bar->parentWidget();
    QSet< QAction* > mainwindowActions;
    if (parWidget) {
        for (QObject* o : qAsConst(parWidget->children())) {
            if (QAction* a = qobject_cast< QAction* >(o)) {
                // 说明是action
                if (!a->objectName().isEmpty()) {
                    mainwindowActions.insert(a);
                }
            }
        }
    }
    // 开始遍历每个category，加入action

    if (nullptr == bar) {
        // 非ribbon模式，直接退出
        return (res);
    }
    QSet< QAction* > categoryActions;
    QList< SARibbonCategory* > categorys = bar->categoryPages();
    int tag                              = AutoCategoryDistinguishBeginTag;

    for (SARibbonCategory* c : qAsConst(categorys)) {
        QList< SARibbonPanel* > panels = c->panelList();
        for (SARibbonPanel* p : qAsConst(panels)) {
            categoryActions += autoRegisteWidgetActions(p, tag, false);
        }
        setTagName(tag, c->categoryName());
        res[ tag ] = c;
        ++tag;
    }
    // 找到不在功能区的actions
    QSet< QAction* > notincategory = mainwindowActions - categoryActions;

    for (QAction* a : qAsConst(notincategory)) {
        if (!a->objectName().isEmpty()) {
            registeAction(a, NotInRibbonCategoryTag, a->objectName(), false);
        }
    }
    if (notincategory.size() > 0) {
        setTagName(NotInRibbonCategoryTag, tr("not in ribbon"));
    }
    for (auto i = res.begin(); i != res.end(); ++i) {
        connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged);
    }
    d_ptr->mTagToCategory = res;
    return (res);
}

/**
 * @brief 自动加载action
 * @param w
 * @param tag
 * @param enableEmit
 * @return 返回成功加入SARibbonActionsManager管理的action
 */
QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit)
{
    QSet< QAction* > res;
    QList< QAction* > was = w->actions();

    for (QAction* a : qAsConst(was)) {
        if (res.contains(a) || a->objectName().isEmpty()) {
            // 重复内容不重复加入
            // 没有object name不加入
            continue;
        }
        if (registeAction(a, tag, a->objectName(), enableEmit)) {
            res.insert(a);
        }
    }
    return (res);
}

/**
 * @brief 根据标题查找action
 * @param text
 * @return
 */
QList< QAction* > SARibbonActionsManager::search(const QString& text)
{
    QList< QAction* > res;

    if (text.isEmpty()) {
        return (res);
    }
    QStringList kws = text.split(" ");

    if (kws.isEmpty()) {
        kws.append(text);
    }

    for (const QString& k : qAsConst(kws)) {
        for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) {
            if (i.key()->text().contains(k, Qt::CaseInsensitive)) {
                res.append(i.key());
            }
        }
    }
    return (res);
}

void SARibbonActionsManager::clear()
{
    d_ptr->clear();
}

/**
 * @brief action 被delete时，将触发此槽把管理的action删除
 * @param o
 * @note 这个函数不会触发actionTagChanged信号
 */
void SARibbonActionsManager::onActionDestroyed(QObject* o)
{
    QAction* act = static_cast< QAction* >(o);

    removeAction(act, false);
}

/**
 * @brief autoRegisteActions函数会关联此槽，在标签内容改变时改变tag 对应 文本
 * @param title
 */
void SARibbonActionsManager::onCategoryTitleChanged(const QString& title)
{
    SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender());

    if (nullptr == c) {
        return;
    }
    int tag = d_ptr->mTagToCategory.key(c, -1);

    if (tag == -1) {
        return;
    }
    setTagName(tag, title);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SARibbonActionsModel
////////////////////////////////////////////////////////////////////////////////////////////////////////

class SARibbonActionsManagerModel::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel)
public:
    PrivateData(SARibbonActionsManagerModel* p);
    void updateRef();
    int count() const;
    QAction* at(int index);
    bool isNull() const;

public:
    SARibbonActionsManager* mMgr { nullptr };
    int mTag { SARibbonActionsManager::CommonlyUsedActionTag };
    QString mSeatchText;
    QList< QAction* > mActions;
};

SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p)
{
}

void SARibbonActionsManagerModel::PrivateData::updateRef()
{
    if (isNull()) {
        return;
    }
    if (!mSeatchText.isEmpty()) {
        mActions = mMgr->search(mSeatchText);
    } else {
        mActions = mMgr->actions(mTag);
    }
}

int SARibbonActionsManagerModel::PrivateData::count() const
{
    if (isNull()) {
        return (0);
    }
    return (mActions.size());
}

QAction* SARibbonActionsManagerModel::PrivateData::at(int index)
{
    if (isNull()) {
        return (nullptr);
    }
    if (index >= count()) {
        return (nullptr);
    }
    return (mActions.at(index));
}

bool SARibbonActionsManagerModel::PrivateData::isNull() const
{
    return (mMgr == nullptr);
}

//===================================================
// SARibbonActionsManagerModel
//===================================================

SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p)
    : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this))
{
}

SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p)
    : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this))
{
    setupActionsManager(m);
}

SARibbonActionsManagerModel::~SARibbonActionsManagerModel()
{
}

int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid()) {  // 非顶层
        return (0);
    }
    // 顶层
    return (d_ptr->count());
}

QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    Q_UNUSED(section);
    if (role != Qt::DisplayRole) {
        return (QVariant());
    }
    if (Qt::Horizontal == orientation) {
        return (tr("action name"));
    }
    return (QVariant());
}

Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const
{
    if (!index.isValid()) {
        return (Qt::NoItemFlags);
    }
    return (Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}

QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const
{
    QAction* act = indexToAction(index);

    if (nullptr == act) {
        return (QVariant());
    }

    switch (role) {
    case Qt::DisplayRole: {
        QString str = act->text();
        if (!str.isEmpty()) {
            return str;
        }
        // 如果文本为空，一般是没有设置文本，或者是窗口，如果是窗口，那么用windowtitle作为文本
        if (QWidgetAction* wa = qobject_cast< QWidgetAction* >(act)) {
            if (QWidget* w = wa->defaultWidget()) {
                str = w->windowTitle();
            }
        }
        return str;
    }
    case Qt::DecorationRole:
        return (act->icon());

    default:
        break;
    }
    return (QVariant());
}

void SARibbonActionsManagerModel::setFilter(int tag)
{
    d_ptr->mTag = tag;
    update();
}

void SARibbonActionsManagerModel::update()
{
    beginResetModel();
    d_ptr->updateRef();
    endResetModel();
}

void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m)
{
    d_ptr->mMgr     = m;
    d_ptr->mTag     = SARibbonActionsManager::CommonlyUsedActionTag;
    d_ptr->mActions = m->filter(d_ptr->mTag);
    connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged);
    update();
}

void SARibbonActionsManagerModel::uninstallActionsManager()
{
    if (!d_ptr->isNull()) {
        disconnect(
            d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged);
        d_ptr->mMgr = nullptr;
        d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag;
    }
    update();
}

QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const
{
    if (!index.isValid()) {
        return (nullptr);
    }
    if (index.row() >= d_ptr->count()) {
        return (nullptr);
    }
    return (d_ptr->at(index.row()));
}

/**
 * @brief 查找
 * @param text
 */
void SARibbonActionsManagerModel::search(const QString& text)
{
    d_ptr->mSeatchText = text;
    update();
}

void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete)
{
    if (isdelete && (tag == d_ptr->mTag)) {
        d_ptr->mTag = SARibbonActionsManager::UnknowActionTag;
        update();
    } else {
        if (tag == d_ptr->mTag) {
            update();
        }
    }
}

/*** End of inlined file: SARibbonActionsManager.cpp ***/

/*** Start of inlined file: SARibbonButtonGroupWidget.cpp ***/
#include <QHBoxLayout>
#include <QDebug>
#include <QMargins>
#include <QChildEvent>
#include <QActionEvent>
#include <QWidgetAction>
#include <QApplication>
#include <QStyle>

//===================================================
// SARibbonButtonGroupWidget
//===================================================

SARibbonButtonGroupWidget::SARibbonButtonGroupWidget(QWidget* parent) : QToolBar(parent)
{
    setAutoFillBackground(false);
    setAttribute(Qt::WA_NoSystemBackground);
    setOrientation(Qt::Horizontal);
    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    setMovable(false);    // 禁止移动
    setFloatable(false);  // 禁止浮动
    setContentsMargins(0, 0, 0, 0);
    const int smallIconSize = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
    setIconSize(QSize(smallIconSize, smallIconSize));
}

SARibbonButtonGroupWidget::~SARibbonButtonGroupWidget()
{
}

/**
 * @brief Add a menu QAction to the button group widget/在按钮栏中添加一个带菜单的 QAction
 *
 * This function is used to add a QAction that has been associated with a menu to the toolbar and set its popup
 * mode.The popup mode of the tool button determines how users interact with the menu.
 *
 * 此函数用于向工具栏添加一个已经关联了菜单的 QAction，并设置其弹出模式。工具按钮的弹出模式决定了用户如何与菜单进行交互。
 *
 * @param menuAction QAction object that has been set with a menu/已经设置了菜单的 QAction 对象
 * @param popupMode Popup mode of the tool button, default is InstantPopup/工具按钮的弹出模式，默认为 InstantPopup
 *
 * @note If menuAction is not associated with a menu, this function will not perform any special processing/如果
 * menuAction 没有关联菜单，此函数不会进行任何特殊处理
 *
 * @note This function will automatically find the corresponding QToolButton and set the popup
 * mode/此函数会自动查找对应的 QToolButton 并设置弹出模式
 *
 * @par Example:/示例:
 * @code
 * QMenu *fileMenu = new QMenu("File");
 * fileMenu->addAction("New");
 * fileMenu->addAction("Open");
 *
 * QAction *fileAction = new QAction("File");
 * fileAction->setMenu(fileMenu);
 *
 * // Add to button group widget/添加到按钮栏
 * buttongroup->addMenuAction(fileAction, QToolButton::InstantPopup);
 * @endcode
 *
 * @see QToolButton::ToolButtonPopupMode
 */
void SARibbonButtonGroupWidget::addMenuAction(QAction* menuAction, QToolButton::ToolButtonPopupMode popupMode)
{
    if (!menuAction) {
        return;
    }

    // 添加动作到工具栏
    addAction(menuAction);

    // 如果动作有关联菜单，则设置工具按钮的弹出模式
    if (menuAction->menu()) {
        QToolButton* toolButton = qobject_cast< QToolButton* >(widgetForAction(menuAction));
        if (toolButton) {
            toolButton->setPopupMode(popupMode);
        }
    }
}

/**
 * @brief Create and add a menu action to the button group widget (text only
 * version)/在按钮栏中创建并添加一个带菜单的动作（仅文本版本）
 *
 * This function creates a new QAction based on the specified text,
 * associates it with the specified menu, adds it to the toolbar, and sets the popup mode.
 *
 * 此函数根据指定的文本创建一个新的 QAction，将其与指定菜单关联，添加到工具栏并设置弹出模式。
 *
 * @param text Button text/按钮文本
 * @param menu Menu object to associate/要关联的菜单对象
 * @param popupMode Popup mode of the tool button, default is InstantPopup/工具按钮的弹出模式，默认为 InstantPopup
 * @return Returns the newly created QAction object, or nullptr if the parameter is invalid/返回新创建的 QAction
 * 对象，如果参数无效则返回 nullptr
 *
 * @note The caller does not need to manually manage the lifecycle of the returned QAction,it will be automatically
 * managed by the toolbar/调用者不需要手动管理返回的 QAction 的生命周期， 它会由工具栏自动管理
 *
 * @par Example:/示例:
 * @code
 * // Create menu/创建菜单
 * QMenu *helpMenu = new QMenu("Help");
 * helpMenu->addAction("Contents");
 * helpMenu->addAction("About");
 *
 * // Add menu to button group widget with custom text/使用自定义文本添加菜单到按钮栏
 * QAction *helpAction = buttongroup->addMenuAction("Help", helpMenu, QToolButton::MenuButtonPopup);
 * @endcode
 *
 * @see QToolButton::ToolButtonPopupMode
 */
QAction* SARibbonButtonGroupWidget::addMenuAction(QMenu* menu, QToolButton::ToolButtonPopupMode popupMode)
{
    if (!menu) {
        return nullptr;
    }
    addMenuAction(menu->menuAction(), popupMode);
    return menu->menuAction();
}

/*** End of inlined file: SARibbonButtonGroupWidget.cpp ***/

/*** Start of inlined file: SARibbonStackedWidget.cpp ***/
#include <QEventLoop>
#include <QResizeEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QPropertyAnimation>
#include <QLayout>
#ifndef SARIBBONSTACKEDWIDGET_DEBUG_PRINT
#define SARIBBONSTACKEDWIDGET_DEBUG_PRINT 0
#endif
#if SARIBBONSTACKEDWIDGET_DEBUG_PRINT
#include <QDebug>
#endif
/**
 * @brief The SARibbonStackedWidgetPrivate class
 */
class SARibbonStackedWidget::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonStackedWidget)
public:
    QEventLoop* eventLoop { nullptr };
    bool useAnimation { false };                ///< 是否使用动画
    QPropertyAnimation* animation { nullptr };  ///< 动画对象
    QRect normalGeometry;                       ///< 正常状态下的几何位置
    bool isAnimating { false };  ///< 标记动画是否正在进行，这个必须要有，它比animation->state()更早标记动画是否启动
    int animationWidgetHeight { 0 };  ///< 动画时的窗口高度
public:
    PrivateData(SARibbonStackedWidget* p) : q_ptr(p)
    {
    }

    bool isAnimationRunning() const
    {
        return (isAnimating || animation->state() == QAbstractAnimation::Running);
    }
};

SARibbonStackedWidget::SARibbonStackedWidget(QWidget* parent)
    : QStackedWidget(parent), d_ptr(new SARibbonStackedWidget::PrivateData(this))
{
    setNormalMode();
    setupAnimation();
}

SARibbonStackedWidget::~SARibbonStackedWidget()
{
}

void SARibbonStackedWidget::setupAnimation()
{
    d_ptr->animation = new QPropertyAnimation(this, "animationWidgetHeight", this);
    d_ptr->animation->setEasingCurve(QEasingCurve::OutQuad);
    d_ptr->animation->setDuration(300);
    connect(d_ptr->animation, &QPropertyAnimation::finished, this, &SARibbonStackedWidget::onAnimationFinished);
}

int SARibbonStackedWidget::animationWidgetHeight() const
{
    return d_ptr->animationWidgetHeight;
}
void SARibbonStackedWidget::setAnimationWidgetHeight(int h)
{
    if (d_ptr->animationWidgetHeight == h) {
        return;
    }

    d_ptr->animationWidgetHeight = h;

    if (d_ptr->isAnimationRunning() && isPopupMode()) {
        // 更新窗口大小和位置
        setFixedSize(d_ptr->normalGeometry.width(), h);
#if SARIBBONSTACKEDWIDGET_DEBUG_PRINT
        qDebug() << "setAnimationWidgetHeight setFixedSize=" << d_ptr->normalGeometry.width() << "," << h;
#endif
    }
}

/**
 * @brief 设置窗口normalGeometry，由于此窗口会有动画，防止动画过程中设置尺寸又被动画覆盖，因此此窗口的尺寸设置使用setNormalSize
 *
 * 此函数在没有动画的时候，等同于
 * @code
 * setFixedSize(normalGeometry.width(),normalGeometry.height());
 * move(normalGeometry.x(),normalGeometry.y());
 * @endcode
 * @param normalGeometry
 */
void SARibbonStackedWidget::setNormalGeometry(const QRect& normalGeometry)
{
    d_ptr->normalGeometry = normalGeometry;
    if (!d_ptr->isAnimationRunning()) {
        setFixedSize(d_ptr->normalGeometry.width(), d_ptr->normalGeometry.height());
        move(d_ptr->normalGeometry.x(), d_ptr->normalGeometry.y());
    }
}

QRect SARibbonStackedWidget::normalGeometry() const
{
    return d_ptr->normalGeometry;
}

/**
 * @brief 对内部窗口发送布局请求
 *
 * 这个方法会让子窗口布局失效同时重新计算布局
 */
void SARibbonStackedWidget::layoutRequestInnerWidgets()
{
    // 确保所有子部件都填满整个区域
    for (int i = 0; i < count(); ++i) {
        QWidget* innerWidget = widget(i);
        if (!innerWidget) {
            continue;
        }
        // 方法1 update（不生效）
        //  innerWidget->update();

        // 方法2 postEvent（不生效）
        //  QApplication::postEvent(innerWidget, new QEvent(QEvent::LayoutRequest));

        // 方法3 invalidate+activate（生效）
        if (auto lay = innerWidget->layout()) {
            lay->invalidate();
            lay->activate();
        }
    }
}

/**
 * @brief 设置弹出模式
 *
 * 在此模式下，窗口将：
 * - 设置为 Qt::Popup 标志
 * - 无边框窗口
 * - 显示为面板样式
 *
 * @note 切换模式时会自动处理窗口隐藏和显示
 */
void SARibbonStackedWidget::setPopupMode()
{
    if (isPopupMode()) {
        return;
    }
    setMouseTracking(true);
    setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
    setFrameShape(QFrame::Panel);
}

/**
 * @brief 检查当前是否处于弹出模式
 * @return 如果是弹出模式返回 true，否则返回 false
 */
bool SARibbonStackedWidget::isPopupMode() const
{
    return (windowFlags() & Qt::Popup);
}

/**
 * @brief 设置正常模式 和普通的stackwidget一样
 *
 * 在此模式下，窗口将：
 * - 设置为常规部件模式 (Qt::Widget)
 * - 无边框窗口
 * - 不显示框架
 *
 * @note 如果存在模态事件循环，将退出该循环
 */
void SARibbonStackedWidget::setNormalMode()
{
    if (isNormalMode()) {
        return;
    }
    if (d_ptr->eventLoop) {
        d_ptr->eventLoop->exit();
        d_ptr->eventLoop = nullptr;
    }
    // 停止动画并恢复最终位置
    if (d_ptr->isAnimationRunning()) {
        d_ptr->animation->stop();
        d_ptr->isAnimating = false;  // 停止后，一定要加上标记
        setFixedHeight(d_ptr->normalGeometry.height());
    }
    setMouseTracking(false);
    setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
    setFrameShape(QFrame::NoFrame);
}

/**
 * @brief 检查当前是否处于正常模式
 * @return 如果是正常模式返回 true，否则返回 false
 */
bool SARibbonStackedWidget::isNormalMode() const
{
    return (!isPopupMode());
}

/**
 * @brief 在弹出模式下以模态方式运行事件循环
 *
 * 此方法仅在弹出模式下有效：
 * 1. 显示窗口
 * 2. 启动局部事件循环
 * 3. 窗口隐藏时退出事件循环
 *
 * @note 在正常模式下调用此方法仅显示窗口
 * @warning 不要在事件循环中再次调用此方法
 */
void SARibbonStackedWidget::exec()
{
    if (!isPopupMode()) {
        show();
        return;
    }
    show();
    QEventLoop event;
    d_ptr->eventLoop = &event;
    event.exec();
    d_ptr->eventLoop = nullptr;  // 确保重置
}

/**
 * @brief 类似tabbar的moveTab函数，交换两个窗口的index
 * @param from
 * @param to
 * @note 此操作会触发widgetRemoved(int index)信号
 */
void SARibbonStackedWidget::moveWidget(int from, int to)
{
    QWidget* w = widget(from);

    removeWidget(w);
    insertWidget(to, w);
}

/**
 * @brief 设置是否启用弹出动画
 * @param on
 */
void SARibbonStackedWidget::setUseAnimation(bool on)
{
    d_ptr->useAnimation = on;
}

/**
 * @brief 获取动画启用状态
 * @return
 */
bool SARibbonStackedWidget::isUseAnimation() const
{
    return d_ptr->useAnimation;
}
/**
 * @brief 设置动画持续时间（毫秒）
 * @param duration
 */
void SARibbonStackedWidget::setAnimationDuration(int duration)
{
    d_ptr->animation->setDuration(duration);
}

/**
 * @brief 获取动画持续时间
 * @return
 */
int SARibbonStackedWidget::animationDuration() const
{
    return d_ptr->animation->duration();
}

/**
 * @brief 动画完成槽函数
 */
void SARibbonStackedWidget::onAnimationFinished()
{
    d_ptr->isAnimating = false;
    if (isPopupMode()) {
        // 完成显示后，把其它窗口的尺寸移动到位置
        if (height() != d_ptr->normalGeometry.height()) {
            // 恢复窗口到正常位置
            setFixedHeight(d_ptr->normalGeometry.height());
        } else {
            updateInnerWidgetGeometry();
        }
    }
}

void SARibbonStackedWidget::showEvent(QShowEvent* e)
{
    if (isPopupMode() && d_ptr->useAnimation && !d_ptr->isAnimationRunning()) {
        // 这个必须放在最前面，否则setFixedHeight(0);就会把子窗口的尺寸改变
        d_ptr->isAnimating = true;
        // 标记为显示动画

        // 设置动画参数
        d_ptr->animation->setStartValue(0);
        d_ptr->animation->setEndValue(d_ptr->normalGeometry.height());

        // 启动动画
        d_ptr->animation->start();
        // 设置起始位置
        setFixedHeight(0);
#if SARIBBONSTACKEDWIDGET_DEBUG_PRINT
        qDebug() << "Starting show animation. Current state:" << d_ptr->animation->state()
                 << "Start value:" << d_ptr->animation->startValue().toInt()
                 << "End value:" << d_ptr->animation->endValue().toInt();
#endif

        // 确保动画已启动
        QCoreApplication::processEvents();
    } else {
        QStackedWidget::showEvent(e);
    }
}
void SARibbonStackedWidget::hideEvent(QHideEvent* e)
{
    if (!isPopupMode()) {
        // 非弹出模式，正常的隐藏
        QStackedWidget::hideEvent(e);
        return;
    }

    if (d_ptr->eventLoop) {
        d_ptr->eventLoop->exit();
        d_ptr->eventLoop = nullptr;
    }
    Q_EMIT hidWindow();
    QStackedWidget::hideEvent(e);
}

void SARibbonStackedWidget::updateInnerWidgetGeometry()
{
    // 确保所有子部件都填满整个区域
    const QSize newSize = size();
    for (int i = 0; i < count(); ++i) {
        QWidget* innerWidget = widget(i);
        if (!innerWidget)
            continue;

        if (i == currentIndex()) {
            // 确保当前部件也填满整个区域
            if (innerWidget->size() != newSize) {
                innerWidget->move(0, 0);
                innerWidget->setFixedSize(newSize);
            }
            continue;
        }

        // 设置非活动部件的位置和大小
        innerWidget->move(0, 0);
        innerWidget->setFixedSize(newSize);
        // innerWidget->setGeometry(0, 0, newSize.width(), newSize.height());
        // 通知部件布局可能需要更新
#if SARIBBONSTACKEDWIDGET_DEBUG_PRINT
        qDebug() << "SARibbonStackedWidget::resizeEvent [not Animation]innerWidget move to:" << innerWidget->geometry();
#endif
        QApplication::postEvent(innerWidget, new QEvent(QEvent::LayoutRequest));
    }
}

void SARibbonStackedWidget::resizeEvent(QResizeEvent* e)
{
    if (d_ptr->isAnimationRunning() && d_ptr->useAnimation) {
        // 正在动画过程中，移动当前窗口的位置
        QWidget* innerWidget = currentWidget();
        if (!innerWidget) {
            return;
        }
        int x = innerWidget->x();
        if (innerWidget->size() != d_ptr->normalGeometry.size()) {
            innerWidget->setFixedSize(d_ptr->normalGeometry.size());
        }
#if SARIBBONSTACKEDWIDGET_DEBUG_PRINT
        qDebug() << "SARibbonStackedWidget::resizeEvent innerWidget geo:" << innerWidget->geometry();
#endif
        innerWidget->move(x, e->size().height() - d_ptr->normalGeometry.height());

#if SARIBBONSTACKEDWIDGET_DEBUG_PRINT
        qDebug() << "SARibbonStackedWidget::resizeEvent innerWidget move to:" << innerWidget->geometry();
#endif

    } else {
        // 没有动画，尺寸的改变记录下来
        d_ptr->normalGeometry = geometry();
        updateInnerWidgetGeometry();
    }
    // 调用基类处理
    QStackedWidget::resizeEvent(e);
}

/*** End of inlined file: SARibbonStackedWidget.cpp ***/

/*** Start of inlined file: SARibbonSeparatorWidget.cpp ***/
#include <QApplication>
#include <QScreen>
#include <QStylePainter>
#include <QPainter>
#include <QDebug>

SARibbonSeparatorWidget::SARibbonSeparatorWidget(QWidget* parent) : QFrame(parent)
{
    setFrameShape(QFrame::VLine);
    setFrameShadow(QFrame::Plain);

    if (QScreen* screen = QApplication::primaryScreen()) {
        qreal dpr           = screen->physicalDotsPerInch() / screen->logicalDotsPerInch();
        int scaledLineWidth = qRound(1.0 * dpr);  // 假设基础 lineWidth 是 1
        if (scaledLineWidth < 1) {
            scaledLineWidth = 1;
        }
        setLineWidth(scaledLineWidth);
        //    qDebug() << "SARibbonSeparatorWidget:" << scaledLineWidth;
    } else {
        setLineWidth(1);
    }
}

SARibbonSeparatorWidget::~SARibbonSeparatorWidget()
{
}

/*** End of inlined file: SARibbonSeparatorWidget.cpp ***/

/*** Start of inlined file: SARibbonCtrlContainer.cpp ***/
#include <QHBoxLayout>
#include <QPainter>
#include <QPaintEvent>
#include <QStylePainter>
#include <QDebug>
#include <QLabel>
#include <QApplication>
#include <QScreen>

/**
 * @brief The SARibbonCtrlContainerPrivate class
 */
class SARibbonCtrlContainer::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonCtrlContainer)
public:
    QWidget* mContainerWidget { nullptr };
    QHBoxLayout* mHorizontalLayout { nullptr };
    QLabel* mLabelPixmap { nullptr };
    QLabel* mLabelText { nullptr };
    QSize mIconSize { 24, 24 };
    QIcon mIcon;
    PrivateData(SARibbonCtrlContainer* p) : q_ptr(p)
    {
        mHorizontalLayout = new QHBoxLayout(p);
        mHorizontalLayout->setSpacing(0);
        mHorizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
        mHorizontalLayout->setContentsMargins(0, 0, 0, 0);

        mLabelPixmap = new QLabel(p);
        mLabelPixmap->setObjectName(QString::fromUtf8("labelPixmap"));
        mLabelPixmap->setAlignment(Qt::AlignCenter);
        mHorizontalLayout->addWidget(mLabelPixmap);

        mLabelText = new QLabel(p);
        mLabelText->setObjectName(QString::fromUtf8("labelText"));
        mHorizontalLayout->addWidget(mLabelText);
    }

    void setContainerWidget(QWidget* w)
    {
        if (mContainerWidget) {
            // 原来有widget
            QWidget* oldwidget = mContainerWidget;
            takeContainerWidget(oldwidget);
            oldwidget->deleteLater();
        }
        mContainerWidget = w;
        if (!w) {
            return;
        }
        if (w->parent() != q_ptr) {
            w->setParent(q_ptr);
        }
        // ContainerWidget永远在最右边
        mHorizontalLayout->insertWidget(mHorizontalLayout->count(), w);
        QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        w->setSizePolicy(sizePolicy);
    }

    void takeContainerWidget(QWidget* w)
    {
        int i = mHorizontalLayout->indexOf(w);
        if (i >= 0) {
            QLayoutItem* item = mHorizontalLayout->takeAt(i);
            delete item;
        }
    }
};

//===================================================
// SARibbonCtrlContainer
//===================================================
SARibbonCtrlContainer::SARibbonCtrlContainer(QWidget* parent)
    : QWidget(parent), d_ptr(new SARibbonCtrlContainer::PrivateData(this))
{
}

SARibbonCtrlContainer::~SARibbonCtrlContainer()
{
}

QWidget* SARibbonCtrlContainer::containerWidget()
{
    return (d_ptr->mContainerWidget);
}

const QWidget* SARibbonCtrlContainer::containerWidget() const
{
    return (d_ptr->mContainerWidget);
}

void SARibbonCtrlContainer::setEnableShowIcon(bool b)
{
    d_ptr->mLabelPixmap->setVisible(b);
}

void SARibbonCtrlContainer::setEnableShowTitle(bool b)
{
    d_ptr->mLabelText->setVisible(b);
}

/**
 * @brief 判断是否存在容器窗口
 * @return
 */
bool SARibbonCtrlContainer::hasContainerWidget() const
{
    return (d_ptr->mContainerWidget != nullptr);
}

/**
 * @brief 设置图标
 * @param i
 */
void SARibbonCtrlContainer::setIcon(const QIcon& i)
{
    d_ptr->mIcon = i;
    // QPixmap pixmap = i.pixmap(d_ptr->mIconSize);
    QPixmap pixmap = SA::iconToPixmap(i, d_ptr->mIconSize, SA::widgetDevicePixelRatio(this));
    d_ptr->mLabelPixmap->setPixmap(pixmap);
}

void SARibbonCtrlContainer::setIcon(const QPixmap& pixmap)
{
    d_ptr->mLabelPixmap->setPixmap(pixmap);
}

/**
 * @brief 获取图标
 * @return
 */
QIcon SARibbonCtrlContainer::icon() const
{
    return d_ptr->mIcon;
}

/**
 * @brief 设置文字
 * @param t
 */
void SARibbonCtrlContainer::setText(const QString& t)
{
    d_ptr->mLabelText->setText(t);
}

/**
 * @brief 获取文字
 * @return
 */
QString SARibbonCtrlContainer::text() const
{
    return d_ptr->mLabelText->text();
}

void SARibbonCtrlContainer::setContainerWidget(QWidget* w)
{
    d_ptr->setContainerWidget(w);
}

/**
 * @brief 获取显示icon的窗口
 * @return
 */
QWidget* SARibbonCtrlContainer::iconWidget() const
{
    return d_ptr->mLabelPixmap;
}

/*** End of inlined file: SARibbonCtrlContainer.cpp ***/

/*** Start of inlined file: SARibbonQuickAccessBar.cpp ***/
#include <QGuiApplication>

//===================================================
// SARibbonQuickAccessBar
//===================================================
SARibbonQuickAccessBar::SARibbonQuickAccessBar(QWidget* parent) : SARibbonButtonGroupWidget(parent)
{
}

SARibbonQuickAccessBar::~SARibbonQuickAccessBar()
{
}

/*** End of inlined file: SARibbonQuickAccessBar.cpp ***/

/*** Start of inlined file: SARibbonTabBar.cpp ***/
#include <QStyleOptionTab>
#include <QFontMetrics>

SARibbonTabBar::SARibbonTabBar(QWidget* parent) : QTabBar(parent), m_tabMargin(6, 0, 0, 0)
{
    setExpanding(false);
}

SARibbonTabBar::~SARibbonTabBar()
{
}

const QMargins& SARibbonTabBar::tabMargin() const
{
    return (m_tabMargin);
}

void SARibbonTabBar::setTabMargin(const QMargins& tabMargin)
{
    m_tabMargin = tabMargin;
}

/**
 * @brief tab的尺寸预估
 *
 * 有别于系统默认的tabbar，SARibbonTabBar的tab高度和tabbar高度一致，且不考虑纵向分布情况
 * @param index
 * @return
 */
QSize SARibbonTabBar::tabSizeHint(int index) const
{
    if (index < 0) {
        return QSize();
    }
    QStyleOptionTab opt;
    initStyleOption(&opt, index);
    int hframe            = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
    const QFontMetrics fm = fontMetrics();

    int widgetWidth = 0;
    int padding     = 0;
    if (!opt.leftButtonSize.isEmpty()) {
        padding += 4;
        widgetWidth += opt.leftButtonSize.width();
    }
    if (!opt.rightButtonSize.isEmpty()) {
        padding += 4;
        widgetWidth += opt.rightButtonSize.width();
    }
    if (!opt.icon.isNull()) {
        padding += 4;
    }
    const int textWidth = fm.size(Qt::TextShowMnemonic, opt.text).width();
    QSize csz           = QSize(textWidth + opt.iconSize.width() + hframe + widgetWidth + padding, height());
    return style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
}

/*** End of inlined file: SARibbonTabBar.cpp ***/

/*** Start of inlined file: SARibbonMenu.cpp ***/
#include <QWidgetAction>

SARibbonMenu::SARibbonMenu(QWidget* parent) : QMenu(parent)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}

SARibbonMenu::SARibbonMenu(const QString& title, QWidget* parent) : QMenu(title, parent)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}

SARibbonMenu::~SARibbonMenu()
{
}

QAction* SARibbonMenu::addRibbonMenu(SARibbonMenu* menu)
{
    return (QMenu::addMenu(menu));
}

SARibbonMenu* SARibbonMenu::addRibbonMenu(const QString& title)
{
    SARibbonMenu* menu = new SARibbonMenu(title, this);

    return (menu);
}

SARibbonMenu* SARibbonMenu::addRibbonMenu(const QIcon& icon, const QString& title)
{
    SARibbonMenu* menu = new SARibbonMenu(title, this);

    menu->setIcon(icon);
    return (menu);
}

QAction* SARibbonMenu::addWidget(QWidget* w)
{
    QWidgetAction* action = new QWidgetAction(this);

    action->setDefaultWidget(w);
    addAction(action);
    return (action);
}

/*** End of inlined file: SARibbonMenu.cpp ***/

/*** Start of inlined file: SARibbonTitleIconWidget.cpp ***/
// SARibbonTitleIconWidget.cpp

#include <QPainter>
#include <QMouseEvent>
#include <QContextMenuEvent>
#include <QApplication>
#include <QStyle>
#include <QScreen>
#include <QWindow>
#include <QAction>
#include <QDebug>
/**
 * @brief SARibbonTitleIconWidget::SARibbonTitleIconWidget
 * 构造函数，初始化标题栏图标控件
 * @param parent 父窗口部件
 */
SARibbonTitleIconWidget::SARibbonTitleIconWidget(QWidget* parent)
    : QWidget(parent), m_window(nullptr), m_iconSize(16, 16), mPadding(1)
{
    setCursor(Qt::ArrowCursor);
    createContextMenu();
}

/**
 * @brief SARibbonTitleIconWidget::setWindow
 * 设置关联的窗口对象，使用QPointer确保安全性
 * @param window 要关联的窗口
 */
void SARibbonTitleIconWidget::setWindow(QWidget* window)
{
    m_window = window;
}

/**
 * @brief SARibbonTitleIconWidget::setIcon
 * 设置显示的图标
 * @param icon 要显示的图标
 */
void SARibbonTitleIconWidget::setIcon(const QIcon& icon)
{
    m_icon = icon;
    update();
}

/**
 * @brief SARibbonTitleIconWidget::setIconSize
 * 设置图标大小
 * @param size 图标尺寸
 */
void SARibbonTitleIconWidget::setIconSize(const QSize& size)
{
    m_iconSize = size;
    update();
}

/**
 * @brief 内边距
 * @return
 */
int SARibbonTitleIconWidget::padding() const
{
    return mPadding;
}

/**
 * @brief 设置内边距
 * @param v
 */
void SARibbonTitleIconWidget::setPadding(int v)
{
    mPadding = v;
}

QSize SARibbonTitleIconWidget::sizeHint() const
{
    return QSize(m_iconSize.width() + mPadding, m_iconSize.height() + mPadding);
}

void SARibbonTitleIconWidget::paintEvent(QPaintEvent* event)
{
    Q_UNUSED(event)

    QPainter painter(this);
    // 绘制图标
    if (!m_icon.isNull()) {
        QSize iconSize = m_iconSize.scaled(rect().size(), Qt::KeepAspectRatio);
        iconSize.setWidth(iconSize.width() - 2 * padding());
        iconSize.setHeight(iconSize.height() - 2 * padding());
        iconSize       = iconSize.expandedTo(QSize(8, 8));
        QRect iconRect = QRect(
            (width() - iconSize.width()) / 2, (height() - iconSize.height()) / 2, iconSize.width(), iconSize.height());
        m_icon.paint(&painter, iconRect);
    }
}

void SARibbonTitleIconWidget::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton && m_window) {
        if (m_contextMenu) {
            QPoint menuPos = mapToGlobal(QPoint(0, height()));
            m_contextMenu->popup(menuPos);
            event->accept();
            return;
        }
    }
    QWidget::mousePressEvent(event);
}

void SARibbonTitleIconWidget::contextMenuEvent(QContextMenuEvent* event)
{
    if (m_contextMenu && m_window) {
        QPoint menuPos = mapToGlobal(event->pos());
        m_contextMenu->popup(menuPos);
        event->accept();
        return;
    }
    QWidget::contextMenuEvent(event);
}

/**
 * @brief 创建上下文菜单，包含标准的窗口控制选项
 */
void SARibbonTitleIconWidget::createContextMenu()
{
    m_contextMenu = new QMenu(this);
    setupMenuActions();
}

/**
 * @brief 设置菜单动作，包括还原、移动、大小、最小化、最大化和关闭操作
 */
void SARibbonTitleIconWidget::setupMenuActions()
{
    // 还原菜单项
    QAction* restoreAction = new QAction(tr("Restore(R)"), this);  // cn:还原
    restoreAction->setIcon(style()->standardIcon(QStyle::SP_TitleBarNormalButton));
    connect(restoreAction, &QAction::triggered, this, &SARibbonTitleIconWidget::onRestore);

    // 移动菜单项
    QAction* moveAction = new QAction(tr("Move(M)"), this);  // cn:移动
    connect(moveAction, &QAction::triggered, this, &SARibbonTitleIconWidget::onMove);

    // 大小菜单项
    QAction* sizeAction = new QAction(tr("Size(S)"), this);  // cn:大小
    connect(sizeAction, &QAction::triggered, this, &SARibbonTitleIconWidget::onSize);

    // 分隔线
    m_contextMenu->addSeparator();

    // 最小化菜单项
    QAction* minimizeAction = new QAction(tr("Minimize(N)"), this);  // cn:最小化
    minimizeAction->setIcon(style()->standardIcon(QStyle::SP_TitleBarMinButton));
    connect(minimizeAction, &QAction::triggered, this, &SARibbonTitleIconWidget::onMinimize);

    // 最大化菜单项
    QAction* maximizeAction = new QAction(tr("Maximize(X)"), this);  // cn:最小化
    maximizeAction->setIcon(style()->standardIcon(QStyle::SP_TitleBarMaxButton));
    connect(maximizeAction, &QAction::triggered, this, &SARibbonTitleIconWidget::onMaximize);

    // 分隔线
    m_contextMenu->addSeparator();

    // 关闭菜单项
    QAction* closeAction = new QAction(tr("Close(C)"), this);  // cn:关闭
    closeAction->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
    connect(closeAction, &QAction::triggered, this, &SARibbonTitleIconWidget::onClose);

    // 添加到菜单
    m_contextMenu->addAction(restoreAction);
    m_contextMenu->addAction(moveAction);
    m_contextMenu->addAction(sizeAction);
    m_contextMenu->addAction(minimizeAction);
    m_contextMenu->addAction(maximizeAction);
    m_contextMenu->addAction(closeAction);
}

/**
 * @brief SARibbonTitleIconWidget::onRestore
 * 还原窗口到正常大小
 */
void SARibbonTitleIconWidget::onRestore()
{
    if (m_window) {
        m_window->showNormal();
    }
}

/**
 * @brief SARibbonTitleIconWidget::onMove
 * 启动窗口移动模式
 */
void SARibbonTitleIconWidget::onMove()
{
    if (m_window) {
        // 在实际应用中，这里可以触发窗口移动逻辑
        // 或者通过其他方式实现移动功能
        m_window->setFocus();
    }
}

/**
 * @brief SARibbonTitleIconWidget::onSize
 * 启动窗口大小调整模式
 */
void SARibbonTitleIconWidget::onSize()
{
    if (m_window) {
        // 在实际应用中，这里可以触发窗口大小调整逻辑
        // 或者通过其他方式实现大小调整功能
        m_window->setFocus();
    }
}

/**
 * @brief SARibbonTitleIconWidget::onMinimize
 * 最小化窗口
 */
void SARibbonTitleIconWidget::onMinimize()
{
    if (m_window) {
        m_window->showMinimized();
    }
}

/**
 * @brief SARibbonTitleIconWidget::onMaximize
 * 切换窗口最大化状态
 */
void SARibbonTitleIconWidget::onMaximize()
{
    if (m_window) {
        if (m_window->isMaximized()) {
            m_window->showNormal();
        } else {
            m_window->showMaximized();
        }
    }
}

/**
 * @brief SARibbonTitleIconWidget::onClose
 * 关闭窗口
 */
void SARibbonTitleIconWidget::onClose()
{
    if (m_window) {
        m_window->close();
    }
}

/*** End of inlined file: SARibbonTitleIconWidget.cpp ***/

/*** Start of inlined file: SARibbonPanelOptionButton.cpp ***/
#include <QAction>

SARibbonPanelOptionButton::SARibbonPanelOptionButton(QWidget* parent) : QToolButton(parent)
{
    setAutoRaise(true);
    setCheckable(false);
    setToolButtonStyle(Qt::ToolButtonIconOnly);
    setIconSize(QSize(10, 10));
    static QIcon s_default_icon = QIcon(":/SARibbon/image/resource/ribbonPanelOptionButton.png");
    setIcon(s_default_icon);
}

SARibbonPanelOptionButton::~SARibbonPanelOptionButton()
{
}

/*** End of inlined file: SARibbonPanelOptionButton.cpp ***/

/*** Start of inlined file: SARibbonPanelItem.cpp ***/
SARibbonPanelItem::SARibbonPanelItem(QWidget* widget)
    : QWidgetItem(widget), rowIndex(-1), columnIndex(-1), action(nullptr), customWidget(false), rowProportion(Large)
{
}

SARibbonPanelItem::~SARibbonPanelItem()
{
}

bool SARibbonPanelItem::isEmpty() const
{
    return (action == nullptr || !action->isVisible());
}

/*** End of inlined file: SARibbonPanelItem.cpp ***/

/*** Start of inlined file: SARibbonPanelLayout.cpp ***/
#include <QWidgetAction>
#include <QQueue>

#ifndef SARibbonPanelLayout_DEBUG_PRINT
#define SARibbonPanelLayout_DEBUG_PRINT 0
#endif

#if SARibbonPanelLayout_DEBUG_PRINT
#ifndef SARibbonPanelLayout_HELP_DRAW_RECT
#define SARibbonPanelLayout_HELP_DRAW_RECT(p, rect)                                                                    \
    do {                                                                                                               \
        p.save();                                                                                                      \
        QPen _pen(Qt::DashLine);                                                                                       \
        _pen.setColor(Qt::blue);                                                                                       \
        p.setPen(_pen);                                                                                                \
        p.setBrush(QBrush());                                                                                          \
        p.drawRect(rect);                                                                                              \
        p.restore();                                                                                                   \
    } while (0)
#endif  // SARibbonPanelLayout_HELP_DRAW_RECT
#endif  // SARibbonPanelLayout_DEBUG_PRINT
SARibbonPanelLayout::SARibbonPanelLayout(QWidget* p) : QLayout(p), mColumnCount(0), mExpandFlag(false), mDirty(true)
{
    setSpacing(1);
    SARibbonPanel* tb = qobject_cast< SARibbonPanel* >(p);

    if (!tb) {
        return;
    }
}

SARibbonPanelLayout::~SARibbonPanelLayout()
{
    // 参考QToolBarLayout
    while (!mItems.isEmpty()) {
        SARibbonPanelItem* item = mItems.takeFirst();
        if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) {
            if (item->customWidget) {
                widgetAction->releaseWidget(item->widget());
            }
        }
        delete item;
    }
}

/**
 * @brief Finds the index of an action in the layout / 在布局中查找一个action的索引
 * @param action The action to find / 要查找的action
 * @return The index of the action, or -1 if not found / action的索引，如果未找到则返回-1
 */
int SARibbonPanelLayout::indexByAction(QAction* action) const
{
    for (int i = 0; i < mItems.count(); ++i) {
        if (mItems.at(i)->action == action) {
            return (i);
        }
    }
    return (-1);
}

/**
 * @brief Gets the SARibbonPanel that owns this layout / 获取拥有此布局的SARibbonPanel
 * @return A pointer to the parent SARibbonPanel, or nullptr if not found / 指向父SARibbonPanel的指针，如果未找到则返回nullptr
 */
SARibbonPanel* SARibbonPanelLayout::ribbonPanel() const
{
    return qobject_cast< SARibbonPanel* >(parentWidget());
}

/**
 * @brief Adds an item to the layout (not supported) / 向布局添加一个项目（不支持）
 *
 * This layout only accepts items created from `QAction` via `insertAction`. Calling this function
 * directly will result in a warning.
 *
 * 此布局仅接受通过 `insertAction` 从 `QAction` 创建的项目。直接调用此函数将导致警告。
 *
 * @param item The layout item to add / 要添加的布局项目
 */
void SARibbonPanelLayout::addItem(QLayoutItem* item)
{
    Q_UNUSED(item);
    qWarning("SARibbonPanelLayout::addItem(): please use addAction() instead");
    return;
}

/**
 * @brief Inserts an action at a specific index / 在指定索引处插入一个action
 *
 * This is the primary method for adding content to the layout. It creates a `SARibbonPanelItem`
 * from the `QAction` and inserts it at the specified position.
 *
 * 这是向布局添加内容的主要方法。它从 `QAction` 创建一个 `SARibbonPanelItem` 并将其插入到指定位置。
 *
 * @param index The index at which to insert the action / 插入action的索引
 * @param act The action to insert / 要插入的action
 * @param rp The row proportion for the action / action的行占比
 */
void SARibbonPanelLayout::insertAction(int index, QAction* act, SARibbonPanelItem::RowProportion rp)
{
    index                   = qMax(0, index);
    index                   = qMin(mItems.count(), index);
    SARibbonPanelItem* item = createItem(act, rp);

    if (item) {
        mItems.insert(index, item);
        // 标记需要重新计算尺寸
        invalidate();
    }
}

/**
 * @brief Sets the option action for the panel / 为面板设置选项action
 *
 * The option action is displayed as a button in the panel's title area.
 * Pass `nullptr` to remove the current option action.
 *
 * 选项action显示在面板标题区域的一个按钮中。
 * 传入 `nullptr` 以移除当前的选项action。
 *
 * @param action The option action, or nullptr to remove / 选项action，或传入nullptr以移除
 */
void SARibbonPanelLayout::setOptionAction(QAction* action)
{
    SARibbonPanel* p = ribbonPanel();
    if (!p) {
        return;
    }
    if (action) {
        // 创建option action
        if (nullptr == mOptionActionBtn) {
            mOptionActionBtn = RibbonSubElementFactory->createRibbonPanelOptionButton(p);
            QObject::connect(mOptionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPanel::actionTriggered);
            // 确保m_optionActionBtn在label之上
            if (mTitleLabel) {
                mTitleLabel->stackUnder(mOptionActionBtn);
            }
        }
        mOptionActionBtn->setDefaultAction(action);
        if (action->icon().isNull()) {
            mOptionActionBtn->setIcon(QIcon(":/SARibbon/image/resource/ribbonPanelOptionButton.png"));
        }
        // 标记需要重新计算尺寸
        invalidate();
    } else {
        // 取消option action
        if (mOptionActionBtn) {
            mOptionActionBtn->hide();
            mOptionActionBtn->deleteLater();
            mOptionActionBtn = nullptr;
            // 标记需要重新计算尺寸
            invalidate();
        }
    }
}

/**
 * @brief Checks if an option action is set / 检查是否设置了选项action
 * @return true if an option action exists; otherwise false / 如果存在选项action则返回true；否则返回false
 */
bool SARibbonPanelLayout::isHaveOptionAction() const
{
    return (mOptionActionBtn != nullptr);
}

/**
 * @brief Retrieves the item at the specified index / 获取指定索引处的项目
 * @param index The index of the item / 项目的索引
 * @return The layout item, or nullptr if index is invalid / 布局项目，如果索引无效则返回nullptr
 */
QLayoutItem* SARibbonPanelLayout::itemAt(int index) const
{
    if ((index < 0) || (index >= mItems.count())) {
        return (nullptr);
    }
    return (mItems.at(index));
}

/**
 * @brief Removes and returns the item at the specified index / 移除并返回指定索引处的项目
 * @param index The index of the item to remove / 要移除的项目的索引
 * @return The removed layout item, or nullptr if index is invalid / 被移除的布局项目，如果索引无效则返回nullptr
 */
QLayoutItem* SARibbonPanelLayout::takeAt(int index)
{
    if ((index < 0) || (index >= mItems.count())) {
        return (nullptr);
    }
    SARibbonPanelItem* item = mItems.takeAt(index);

    QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action);

    if ((widgetAction != 0) && item->customWidget) {
        widgetAction->releaseWidget(item->widget());
    } else {
        // destroy the QToolButton/QToolBarSeparator
        item->widget()->hide();
        item->widget()->deleteLater();
    }

    invalidate();
    return (item);
}

/**
 * @brief Gets the number of items in the layout / 获取布局中项目的数量
 * @return The item count / 项目数量
 */
int SARibbonPanelLayout::count() const
{
    return (mItems.count());
}

/**
 * @brief Checks if the layout is empty / 检查布局是否为空
 * @return true if the layout has no items; otherwise false / 如果布局没有项目则返回true；否则返回false
 */
bool SARibbonPanelLayout::isEmpty() const
{

    return (mItems.isEmpty());
}

/**
 * @brief Invalidates the layout, marking it as dirty / 使布局失效，将其标记为“脏”
 *
 * This forces a recalculation of the layout geometry on the next update.
 *
 * 这会强制在下次更新时重新计算布局几何形状。
 */
void SARibbonPanelLayout::invalidate()
{
    mDirty = true;
    QLayout::invalidate();
}

/**
 * @brief Returns the directions in which the layout can expand / 返回布局可以扩展的方向
 * @return The expanding directions (always Qt::Horizontal for this layout) / 扩展方向（此布局始终为Qt::Horizontal）
 */
Qt::Orientations SARibbonPanelLayout::expandingDirections() const
{
    return (Qt::Horizontal);
}

QSize SARibbonPanelLayout::minimumSize() const
{
    return (mSizeHint);
}

QSize SARibbonPanelLayout::sizeHint() const
{
#if SARibbonPanelLayout_DEBUG_PRINT
    if (SARibbonPanel* panel = ribbonPanel()) {
        qDebug() << "| |-SARibbonPanelLayout sizeHint,sizeHint = " << m_sizeHint;
    }
#endif
    return (mSizeHint);
}

/**
 * @brief Retrieves the SARibbonPanelItem associated with an action / 获取与action关联的SARibbonPanelItem
 * @param action The action to query / 要查询的action
 * @return The associated item, or nullptr if not found / 关联的项目，如果未找到则返回nullptr
 */
SARibbonPanelItem* SARibbonPanelLayout::panelItem(QAction* action) const
{
    int index = indexByAction(action);

    if (index >= 0) {
        return (mItems[ index ]);
    }
    return (nullptr);
}

/**
 * @brief Gets the last item added to the layout / 获取最后添加到布局的项目
 * @return The last item, or nullptr if the layout is empty / 最后一个项目，如果布局为空则返回nullptr
 */
SARibbonPanelItem* SARibbonPanelLayout::lastItem() const
{
    if (mItems.isEmpty()) {
        return (nullptr);
    }
    return (mItems.last());
}

/**
 * @brief Gets the widget associated with the last item / 获取与最后一个项目关联的窗口部件
 * @return The widget, or nullptr if not found / 窗口部件，如果未找到则返回nullptr
 */
QWidget* SARibbonPanelLayout::lastWidget() const
{
    SARibbonPanelItem* item = lastItem();

    if (item) {
        return (item->widget());
    }
    return (nullptr);
}

/**
 * @brief Moves an item from one index to another / 将一个项目从一个索引移动到另一个索引
 * @param from The current index of the item / 项目的当前索引
 * @param to The new index for the item / 项目的新索引
 */
void SARibbonPanelLayout::move(int from, int to)
{
    if (from == to) {
        return;
    }
    if (to < 0) {
        to = 0;
    }
    if (to >= count()) {
        to = count() - 1;
    }
    mItems.move(from, to);
    invalidate();
}

/**
 * @brief 判断是否需要重新布局
 * @return
 */
bool SARibbonPanelLayout::isDirty() const
{
    return (mDirty);
}

void SARibbonPanelLayout::updateGeomArray()
{
    updateGeomArray(geometry());
}

/**
 * @brief 布局所有action
 */
void SARibbonPanelLayout::doLayout()
{
#if SARibbonPanelLayout_DEBUG_PRINT
    if (SARibbonPanel* panel = ribbonPanel()) {
        qDebug() << "| |-SARibbonPanelLayout layoutActions,panel name = " << panel->panelName();
    }
#endif
    if (isDirty()) {
        updateGeomArray();
    }
    QList< QWidget* > showWidgets, hideWidgets;
    SARibbonPanel* panel = ribbonPanel();
    for (SARibbonPanelItem* item : qAsConst(mItems)) {
        if (item->isEmpty()) {
            hideWidgets << item->widget();
        } else {
            // 在category发现item->setGeometry有点奇怪的现象，这里统一使用item->widget->setgeo
            // item->setGeometry(item->itemWillSetGeometry);
            if (item->widget()) {
                item->widget()->setGeometry(item->itemWillSetGeometry);
            }
            showWidgets << item->widget();
        }
    }

    // 不在上面那里进行show和hide因为这会触发SARibbonPanelLayout的重绘，导致循环绘制，非常影响效率
    for (QWidget* w : qAsConst(showWidgets)) {
        if (!w->isVisible())
            w->show();
    }
    for (QWidget* w : qAsConst(hideWidgets)) {
        if (w->isVisible())
            w->hide();
    }
    // 布局label
    if (mTitleLabel) {
        if (isEnableShowPanelTitle()) {
            mTitleLabel->setGeometry(mTitleLabelGeometry);
            if (!mTitleLabel->isVisibleTo(panel)) {
                mTitleLabel->show();
            }
        } else {
            if (mTitleLabel->isVisibleTo(panel)) {
                mTitleLabel->hide();
            }
        }
    }
    // 布局m_optionActionBtn
    if (mOptionActionBtn) {
        mOptionActionBtn->setGeometry(mOptionActionBtnGeometry);
        mOptionActionBtn->setIconSize(QSize(mOptionActionBtnGeometry.width(), mOptionActionBtnGeometry.height()));
    }
}

/**
 * @brief 把action转换为item
 *
 * 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action)
 *
 * 对于普通QAction，此函数会创建SARibbonToolButton，SARibbonToolButton的类型参考SARibbonPanelItem::RowProportion，
 * @param action
 * @param rp 行高占比情况
 * @return 转换的SARibbonPanelItem
 * @note 每个SARibbonPanelItem最终都会携带一个widget，传入的是QWidgetAction的话，会直接使用QWidgetAction带的widget，
 * 否则会内部生成一个SARibbonToolButton
 *
 */
SARibbonPanelItem* SARibbonPanelLayout::createItem(QAction* action, SARibbonPanelItem::RowProportion rp)
{
    bool customWidget    = false;
    QWidget* widget      = nullptr;
    SARibbonPanel* panel = qobject_cast< SARibbonPanel* >(parentWidget());

    if (!panel) {
        // 在没有panel这个函数会返回nullptr
        return (nullptr);
    }
    if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) {
        widget = widgetAction->requestWidget(panel);
        if (widget != nullptr) {
            widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
            customWidget = true;  // 标记为true，在移除的时候是不会对这个窗口进行删除，false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton
            // 明确设置父对象，确保 releaseWidget 时能正确工作
            widget->setParent(panel);
        }
    } else if (action->isSeparator()) {
        SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(panel);
        widget                       = sep;
    }
    // 不是widget，自动生成SARibbonToolbutton
    if (!widget) {
        SARibbonToolButton::RibbonButtonType buttonType =
            ((rp == SARibbonPanelItem::Large) ? SARibbonToolButton::LargeButton : SARibbonToolButton::SmallButton);

        SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(panel);
        button->setFocusPolicy(Qt::NoFocus);
        button->setButtonType(buttonType);
        button->setDefaultAction(action);
        button->setIconSize(mSmallToolButtonIconSize);
        button->setLargeIconSize(mLargeToolButtonIconSize);
        button->setEnableWordWrap(isEnableWordWrap());
        button->setButtonMaximumAspectRatio(mButtonMaximumAspectRatio);
        // 属性设置
        QToolButton::ToolButtonPopupMode popMode = SARibbonPanel::getActionToolButtonPopupModeProperty(action);
        button->setPopupMode(popMode);
        // 根据QAction的属性设置按钮的大小

        QObject::connect(button, &SARibbonToolButton::triggered, panel, &SARibbonPanel::actionTriggered);
        widget = button;
    }
    // 这时总会有widget
    widget->hide();
    SARibbonPanelItem* result = new SARibbonPanelItem(widget);

    result->rowProportion = rp;
    result->customWidget  = customWidget;
    result->action        = action;
    return (result);
}

/**
 * @brief 更新尺寸
 */
void SARibbonPanelLayout::updateGeomArray(const QRect& setrect)
{
    SARibbonPanel* panel = qobject_cast< SARibbonPanel* >(parentWidget());

    if (!panel) {
        return;
    }

    const int height     = setrect.height();
    const QMargins& mag  = contentsMargins();
    const int spacing    = this->spacing();
    const int spacingRow = 1;  // 高度间距，也就是行间距，不同行之间的距离
    int x                = mag.left();
    const int yBegin     = mag.top();
    int titleH           = (mTitleHeight >= 0) ? mTitleHeight : 0;  // 防止负数影响
    int titleSpace       = (mTitleHeight >= 0) ? mTitleSpace : 0;   // 对于没有标题的情况，spacing就不生效
    if (!isEnableShowPanelTitle()) {
        titleH     = 0;
        titleSpace = 0;
    }
    // 获取panel的布局模式 3行或者2行
    //  rowcount 是ribbon的行，有2行和3行两种
    const short rowCount = (panel->panelLayoutMode() == SARibbonPanel::ThreeRowMode) ? 3 : 2;
    // largeHeight是对应large占比的高度
    const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace;
    const int yTitleBegin = height - mag.bottom() - titleH;
    mLargeHeight          = largeHeight;
    // 计算smallHeight的高度
    const int smallHeight = (largeHeight - (rowCount - 1) * spacingRow) / rowCount;
    // Medium行的y位置
    const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3));
    const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacingRow)
                                            : (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight);
    // Small行的y位置
    const int ySmallRow0 = yBegin;
    const int ySmallRow1 = yBegin + smallHeight + spacingRow;
    const int ySmallRow2 = yBegin + 2 * (smallHeight + spacingRow);
    // row用于记录下个item应该属于第几行，item->rowIndex用于记录当前处于第几行，
    // item->rowIndex主要用于SARibbonPanelItem::Medium
    short row  = 0;
    int column = 0;
    // 记录每列最大的宽度
    int columMaxWidth = 0;
    // 记录总宽度
    int totalWidth = 0;

    int itemCount = mItems.count();

#if SARibbonPanelLayout_DEBUG_PRINT
    QString debug_print__log__;
#endif
    // 本列第一、二行占比
    SARibbonPanelItem::RowProportion thisColumnRP0 = SARibbonPanelItem::None;
    SARibbonPanelItem* lastGeomItem                = nullptr;  // 记录最后一个设置位置的item
    for (int i = 0; i < itemCount; ++i) {
        SARibbonPanelItem* item = mItems.at(i);
        if (item->isEmpty()) {
            // 如果是hide就直接跳过
            item->rowIndex    = -1;
            item->columnIndex = -1;
            continue;
        }
        // 当开始新的一列时，重置列宽
        if (row == 0) {
            columMaxWidth = 0;
        }
        QSize hint = item->sizeHint();
#if SARibbonPanelLayout_DEBUG_PRINT
        if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) {
            auto ss__ = tb->sizeHint();
            debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n")
                                      .arg(i)
                                      .arg(ss__.width())
                                      .arg(ss__.height())
                                      .arg(tb->text());
        }
#endif
        Qt::Orientations exp = item->expandingDirections();
        if (item->widget()) {
            // 有窗口是水平扩展，则标记为扩展
            if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) {
                mExpandFlag = true;
            }
        }
        SARibbonPanelItem::RowProportion rp = item->rowProportion;
        if (SARibbonPanelItem::None == rp) {
            // 为定义行占比但是垂直扩展，就定义为Large占比，否则就是small占比
            if (exp & Qt::Vertical) {
                rp = SARibbonPanelItem::Large;
            } else {
                rp = SARibbonPanelItem::Small;
            }
        }
        // 开始根据占比和layoutmode来布局
        switch (rp) {
        case SARibbonPanelItem::Large: {
            // ！！在Large，如果不是处于新列的第一行，就需要进行换列处理
            // 把large一直设置在下一列的开始
            if (row != 0) {
                x += (columMaxWidth + spacing);
                ++column;
            }
            //
            item->rowIndex            = 0;
            item->columnIndex         = column;
            item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight);
            columMaxWidth             = hint.width();
            // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
            x += (columMaxWidth + spacing);
            row           = 0;
            columMaxWidth = 0;
            ++column;
        } break;

        case SARibbonPanelItem::Medium: {
            // 2行模式下Medium和small等价
            if (2 == rowCount) {
                if (0 == row) {
                    item->rowIndex            = 0;
                    item->columnIndex         = column;
                    item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
                    thisColumnRP0             = SARibbonPanelItem::Medium;
                    columMaxWidth             = hint.width();
                    // 下个row为1
                    row = 1;
                    // x不变
                } else {
                    item->rowIndex            = 1;
                    item->columnIndex         = column;
                    item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
                    // 和上个进行比较得到最长宽度
                    columMaxWidth = qMax(columMaxWidth, hint.width());
                    // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
                    x += (columMaxWidth + spacing);
                    row           = 0;
                    columMaxWidth = 0;
                    ++column;
                }
            } else {
                // 3行模式
                if (0 == row) {
                    item->rowIndex            = 0;
                    item->columnIndex         = column;
                    item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
                    thisColumnRP0             = SARibbonPanelItem::Medium;
                    columMaxWidth             = hint.width();
                    row                       = 1;
                    // x不变
                } else if (1 == row) {
                    item->rowIndex            = 1;
                    item->columnIndex         = column;
                    item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
                    columMaxWidth             = qMax(columMaxWidth, hint.width());
                    // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
                    x += (columMaxWidth + spacing);
                    row           = 0;
                    columMaxWidth = 0;
                    ++column;
                } else {
                    // 这种模式一般情况会发生在当前列前两行是Small，添加了一个Medium
                    // 这时需要先换列
                    // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
                    x += (columMaxWidth + spacing);
                    ++column;
                    // 换列后此时等价于0 == row
                    item->rowIndex            = 0;
                    item->columnIndex         = column;
                    item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
                    thisColumnRP0             = SARibbonPanelItem::Medium;
                    columMaxWidth             = hint.width();
                    row                       = 1;
                }
            }
        } break;

        case SARibbonPanelItem::Small: {
            if (0 == row) {
                // 第一行
                item->rowIndex            = 0;
                item->columnIndex         = column;
                item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight);
                thisColumnRP0             = SARibbonPanelItem::Small;
                columMaxWidth             = hint.width();
                // 下个row为1
                row = 1;
                // x不变
            } else if (1 == row) {
                // 第二行
                item->rowIndex            = 1;
                item->columnIndex         = column;
                item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight);
                if ((3 == rowCount) && (SARibbonPanelItem::Medium == thisColumnRP0)) {
                    // 三行模式，并且第一行是Medium
                    item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
                }
                // 和上个进行比较得到最长宽度
                columMaxWidth = qMax(columMaxWidth, hint.width());
                // 这里要看两行还是三行，确定是否要换列
                if (2 == rowCount) {
                    // 两行模式，换列
                    // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
                    x += (columMaxWidth + spacing);
                    row           = 0;
                    columMaxWidth = 0;
                    ++column;
                } else {
                    // 三行模式，继续增加行数
                    row = 2;
                    // x不变
                }
                if ((3 == rowCount) && (SARibbonPanelItem::Medium == thisColumnRP0)) {
                    // 三行模式，并且第一行是Medium，换列
                    // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
                    x += (columMaxWidth + spacing);
                    row           = 0;
                    columMaxWidth = 0;
                    ++column;
                }
            } else {
                // 第三行
                item->rowIndex            = 2;
                item->columnIndex         = column;
                item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight);
                // 和上个进行比较得到最长宽度
                columMaxWidth = qMax(columMaxWidth, hint.width());
                // 换列，x自动递增到下个坐标，列数增加，行数归零，最大列宽归零
                x += (columMaxWidth + spacing);
                row           = 0;
                columMaxWidth = 0;
                ++column;
            }
        } break;

        default:
            // 不可能出现
            break;
        }
        lastGeomItem = item;
    }
    // 最后一个元素，更新列数
    //  2022-06-20 此句本来在循环里面，如果最后一个元素隐藏，会导致无法到达此判断导致异常
    if (lastGeomItem) {  // 最后一个元素，更新totalWidth
        if (lastGeomItem->columnIndex != column) {
            // 说明最后一个元素处于最后位置，触发了换列，此时真实列数需要减1，直接等于column索引
            mColumnCount = column;
            // 由于最后一个元素触发了换列，x值是新一列的位置，直接作为totalWidth要减去已经加入的spacing
            totalWidth = x - spacing + mag.right();
        } else {
            // 说明最后一个元素处于非最后位置，没有触发下一个换列，此时真实列数等于column索引+1
            mColumnCount = column + 1;
            // 由于最后一个元素未触发换列，需要计算totalWidth
            totalWidth = x + columMaxWidth + mag.right();
        }
    }

    // 在设置完所有窗口后，再设置扩展属性的窗口
    if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) {
        // 说明可以设置扩展属性的窗口
        recalcExpandGeomArray(setrect);
    }
    // 布局label
    bool isTitleWidthThanPanel = false;
    if (isEnableShowPanelTitle()) {
        mTitleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH);
        // 这里要确认标题宽度是否大于totalWidth，如果大于，则要把标题的宽度作为totalwidth
        QFontMetrics fm = mTitleLabel->fontMetrics();
        int textWidth   = SA_FONTMETRICS_WIDTH(fm, panel->panelName());
        textWidth += 4;
        if (totalWidth < textWidth) {
            totalWidth            = textWidth;
            isTitleWidthThanPanel = true;  // 说明标题的长度大于按钮布局的长度
        }
    }
    // 布局optionActionButton

    if (isHaveOptionAction()) {
        QSize optBtnSize = optionActionButtonSize();
        if (isEnableShowPanelTitle()) {
            // 有标题
            mOptionActionBtnGeometry.setRect(mTitleLabelGeometry.right() - mTitleLabelGeometry.height(),
                                             mTitleLabelGeometry.y(),
                                             mTitleLabelGeometry.height(),
                                             mTitleLabelGeometry.height());

            // 特殊情况，如果panel的标题长度大于totalWidth，那么说明totalWidth比较短
            // 这时候，optionActionBtn的宽度要加上到标题宽度上
            if (isTitleWidthThanPanel) {
                // 由于文字是居中对齐，因此要扩展2个按钮的宽度
                totalWidth += (2 * titleH);
            }
        } else {
            // 无标题
            mOptionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(),
                                             setrect.bottom() - optBtnSize.height() - mag.bottom(),
                                             optBtnSize.width(),
                                             optBtnSize.height());
            totalWidth += optBtnSize.width();
        }
    }
    // 刷新sizeHint
    int heightHint  = SARibbonPanel::panelHeightHint(panel->fontMetrics(), panel->panelLayoutMode(), titleH);
    this->mSizeHint = QSize(totalWidth, heightHint);
#if SARibbonPanelLayout_DEBUG_PRINT
    qDebug() << "| |-SARibbonPanelLayout updateGeomArray(" << setrect << "),panel name = " << panel->panelName()
             << "\n| | |-size hint =" << this->m_sizeHint  //
             << "\n| | |-totalWidth=" << totalWidth        //
             << "\n| | |-last x=" << x                     //
             << "\n| | |-columMaxWidth=" << columMaxWidth  //
             << "\n| | |-spacing=" << spacing              //
             << "\n| | |-mag=" << mag                      //
             << "\n| | |-largeHeight=" << largeHeight      //
             << "\n| | |-smallHeight=" << smallHeight      //
        ;
    qDebug().noquote() << debug_print__log__;
#endif
}

void SARibbonPanelLayout::recalcExpandGeomArray(const QRect& setrect)
{
    // 计算能扩展的尺寸
    int expandwidth = setrect.width() - this->mSizeHint.width();

    if (expandwidth <= 0) {
        // 没有必要设置
        return;
    }
    // 列扩展信息
    struct _columnExpandInfo
    {
        int oldColumnWidth      = 0;   ///< 原来的列宽
        int columnMaximumWidth  = -1;  ///< 列的最大宽度
        int columnExpandedWidth = 0;   ///< 扩展后列的宽度
        QList< SARibbonPanelItem* > expandItems;
    };
    // 此变量用于记录可以水平扩展的列和控件，在布局结束后，如果还有空间，就把水平扩展的控件进行扩展
    QMap< int, _columnExpandInfo > columnExpandInfo;

    for (SARibbonPanelItem* item : qAsConst(mItems)) {
        if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) {
            // 只获取可见的
            QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex);
            if (i == columnExpandInfo.end()) {
                i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo());
            }
            i.value().expandItems.append(item);
        }
    }
    if (columnExpandInfo.size() <= 0) {
        // 没有需要扩展的就退出
        return;
    }
    // 获取完可扩展的列和控件后，计算对应的列的尺寸
    // 计算能扩展的尺寸
    int oneColCanexpandWidth = expandwidth / columnExpandInfo.size();

    for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) {
        int& oldColumnWidth     = i.value().oldColumnWidth;
        int& columnMaximumWidth = i.value().columnMaximumWidth;
        this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth);
        if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) {
            // 如果小于0说明没有这个列，这种属于异常，删除继续
            //  oldColumnWidth > columnMaximumWidth也是异常
            i = columnExpandInfo.erase(i);
            continue;
        }
        // 开始调整
        int colwidth = oneColCanexpandWidth + oldColumnWidth;  // 先扩展了
        if (colwidth >= columnMaximumWidth) {
            // 过最大宽度要求
            i.value().columnExpandedWidth = columnMaximumWidth;
        } else {
            i.value().columnExpandedWidth = colwidth;
        }
        ++i;
    }
    // 从新调整尺寸
    // 由于会涉及其他列的变更，因此需要所有都遍历一下
    for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) {
        int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth;
        for (SARibbonPanelItem* item : qAsConst(mItems)) {
            if (item->isEmpty() || (item->columnIndex < i.key())) {
                // 之前的列不用管
                continue;
            }
            if (item->columnIndex == i.key()) {
                // 此列的扩展
                if (i.value().expandItems.contains(item)) {
                    // 此列需要扩展的item才扩展尺寸
                    item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth);
                } else {
                    // 此列不扩展的模块保持原来的尺寸
                    continue;
                }
            } else {
                // 后面的移动
                item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen);
            }
        }
    }
#if SARibbonPanelLayout_DEBUG_PRINT
    qDebug() << "| |-SARibbonPanelLayout recalcExpandGeomArray(" << setrect
             << ") panelName=" << ribbonPanel()->panelName()  //
             << ",expandwidth=" << expandwidth                //
        ;
#endif
}

/**
 * @brief 根据列数，计算窗口的宽度，以及最大宽度
 * @param colindex
 * @param width 如果传入没有这个列，返回-1
 * @param maximum 如果传入没有这个列，返回-1
 */
void SARibbonPanelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const
{
    width   = -1;
    maximum = -1;
    for (SARibbonPanelItem* item : mItems) {
        if (!item->isEmpty() && (item->columnIndex == colindex)) {
            width   = qMax(width, item->itemWillSetGeometry.width());
            maximum = qMax(maximum, item->widget()->maximumWidth());
        }
    }
}

/**
 * @brief Gets the panel's title label / 获取面板的标题标签
 * @return A pointer to the SARibbonPanelLabel / 指向SARibbonPanelLabel的指针
 */
SARibbonPanelLabel* SARibbonPanelLayout::panelTitleLabel() const
{
    return mTitleLabel;
}

/**
 * @brief Sets the default icon size for tool buttons / 设置工具按钮的默认图标尺寸
 * @param s The new icon size / 新的图标尺寸
 * @sa toolButtonIconSize
 */
void SARibbonPanelLayout::setToolButtonIconSize(const QSize& smallSize, const QSize& largeSize)
{
    mSmallToolButtonIconSize = smallSize;
    mLargeToolButtonIconSize = largeSize;
}

/**
 * @brief Gets the default icon size for tool buttons / 获取工具按钮的默认图标尺寸
 * @return The current icon size / 当前的图标尺寸
 * @sa setToolButtonIconSize
 */
QPair< QSize, QSize > SARibbonPanelLayout::toolButtonIconSize() const
{
    return qMakePair(mSmallToolButtonIconSize, mLargeToolButtonIconSize);
}

/**
 * @brief 设置大按钮图标尺寸
 * @param largeSize
 */
void SARibbonPanelLayout::setLargeIconSize(const QSize& largeSize)
{
    mLargeToolButtonIconSize = largeSize;
}

/**
 * @brief 大按钮图标尺寸
 * @return
 */
QSize SARibbonPanelLayout::largeIconSize() const
{
    return mLargeToolButtonIconSize;
}

/**
 * @brief 设置小按钮图标尺寸
 * @param largeSize
 */
void SARibbonPanelLayout::setSmallIconSize(const QSize& largeSize)
{
    mSmallToolButtonIconSize = largeSize;
}

/**
 * @brief 小按钮图标尺寸
 * @return
 */
QSize SARibbonPanelLayout::smallIconSize() const
{
    return mSmallToolButtonIconSize;
}

/**
 * @brief Gets the size of the option action button / 获取选项action按钮的尺寸
 * @return The button's size / 按钮的尺寸
 */
QSize SARibbonPanelLayout::optionActionButtonSize() const
{
    return (isEnableShowPanelTitle() ? QSize(12, 12) : QSize(mTitleHeight, mTitleHeight));
}

/**
 * @brief Sets the panel's title label / 设置面板的标题标签
 * @param newTitleLabel The new title label widget / 新的标题标签窗口部件
 */
void SARibbonPanelLayout::setPanelTitleLabel(SARibbonPanelLabel* newTitleLabel)
{
    mTitleLabel = newTitleLabel;
    // 确保m_optionActionBtn在label之上
    if (mOptionActionBtn) {
        if (mTitleLabel) {
            mTitleLabel->stackUnder(mOptionActionBtn);
        }
    }
}

/**
 * @brief 是否允许文字换行
 * @return
 */
bool SARibbonPanelLayout::isEnableWordWrap() const
{
    return mEnableWordWrap;
}

/**
 * @brief 设置文字允许换行
 * @param enableWordWrap
 */
void SARibbonPanelLayout::setEnableWordWrap(bool on)
{
    mEnableWordWrap = on;
    // 遍历所有SARibbonToolButton
    for (SARibbonPanelItem* item : qAsConst(mItems)) {
        if (!item) {
            continue;
        }
        if (SARibbonToolButton* toolbtn = qobject_cast< SARibbonToolButton* >(item->widget())) {
            toolbtn->setEnableWordWrap(on);
        }
    }
}

/**
 * @brief 设置按钮最大宽高比，这个系数决定按钮的最大宽度
 *
 * 按钮的最大宽度为按钮高度*此系数，例如按钮高度为h，那么按钮最大宽度maxw=h*buttonMaximumAspectRatio
 * 如果在此宽度下文字还无法完全显示，那么按钮将不会继续横向扩展，将使用...替代未完全显示的文字
 *
 * @see buttonMaximumAspectRatio
 *
 * @note 用户不应该调用@ref SARibbonPanelLayout::setButtonMaximumAspectRatio 来设置，
 * 而是调用@ref SARibbonBar::setButtonMaximumAspectRatio 设置宽高比
 */
void SARibbonPanelLayout::setButtonMaximumAspectRatio(qreal fac)
{
    mButtonMaximumAspectRatio = fac;
    // 遍历所有SARibbonToolButton
    for (SARibbonPanelItem* item : qAsConst(mItems)) {
        if (!item) {
            continue;
        }
        if (SARibbonToolButton* toolbtn = qobject_cast< SARibbonToolButton* >(item->widget())) {
            toolbtn->setButtonMaximumAspectRatio(fac);
        }
    }
}

/**
 * @brief 按钮最大宽高比，这个系数决定按钮的最大宽度
 * @return 按钮最大宽高比
 * @see setButtonMaximumAspectRatio
 */
qreal SARibbonPanelLayout::buttonMaximumAspectRatio() const
{
    return mButtonMaximumAspectRatio;
}

/**
 * @brief Gets the spacing between the title and the buttons / 获取标题与按钮之间的间距
 * @return The current spacing value / 当前的间距值
 * @sa setPanelTitleSpace
 */
int SARibbonPanelLayout::panelTitleSpace() const
{
    return mTitleSpace;
}

/**
 * @brief Sets the spacing between the title and the buttons / 设置标题与按钮之间的间距
 * @param newTitleSpace The new spacing value / 新的间距值
 * @sa panelTitleSpace
 */
void SARibbonPanelLayout::setPanelTitleSpace(int newTitleSpace)
{
    if (mTitleSpace == newTitleSpace) {
        return;
    }
    mTitleSpace = newTitleSpace;
    invalidate();
}

/**
 * @brief Gets the height of the panel's title / 获取面板标题的高度
 * @return The current title height / 当前的标题高度
 * @sa setPanelTitleHeight
 */
int SARibbonPanelLayout::panelTitleHeight() const
{
    return mTitleHeight;
}

/**
 * @brief Sets the height of the panel's title / 设置面板标题的高度
 * @param newTitleHeight The new title height / 新的标题高度
 * @sa panelTitleHeight
 */
void SARibbonPanelLayout::setPanelTitleHeight(int newTitleHeight)
{
    if (mTitleHeight == newTitleHeight) {
        return;
    }
    mTitleHeight = newTitleHeight;
    invalidate();
}

/**
 * @brief Checks if the panel's title is enabled for display / 检查面板标题是否启用显示
 * @return true if the title is enabled; otherwise false / 如果标题启用则返回true；否则返回false
 * @sa setEnableShowPanelTitle
 */
bool SARibbonPanelLayout::isEnableShowPanelTitle() const
{
    return mEnableShowTitle;
}

/**
 * @brief Sets whether the panel's title is enabled for display / 设置面板标题是否启用显示
 * @param on If true, the title is enabled / 如果为true，则启用标题
 * @sa isEnableShowPanelTitle
 */
void SARibbonPanelLayout::setEnableShowPanelTitle(bool on)
{
    if (mEnableShowTitle == on) {
        return;
    }
    mEnableShowTitle = on;
    invalidate();
}

/**
 * @brief Gets the height of large buttons / 获取大按钮的高度
 * @return The large button height / 大按钮高度
 */
int SARibbonPanelLayout::largeButtonHeight() const
{
    return mLargeHeight;
}

void SARibbonPanelLayout::setGeometry(const QRect& rect)
{
    QRect old = geometry();
    if (old == rect) {
        return;
    }
#if SARibbonPanelLayout_DEBUG_PRINT
    qDebug() << "| |----->SARibbonPanelLayout.setGeometry(" << rect << "(" << ribbonPanel()->panelName() << ")=======";
#endif
    QLayout::setGeometry(rect);
    mDirty = false;
    updateGeomArray(rect);
    doLayout();
}

/*** End of inlined file: SARibbonPanelLayout.cpp ***/

/*** Start of inlined file: SARibbonPanel.cpp ***/
#include <QAction>
#include <QApplication>
#include <QDebug>
#include <QFontMetrics>
#include <QGridLayout>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QResizeEvent>
#include <QWidgetAction>

#ifndef SARIBBONPANEL_DEBUG_PRINT
#define SARIBBONPANEL_DEBUG_PRINT 0
#endif

#if SARIBBONPANEL_DEBUG_PRINT
#ifndef SARIBBONPANELLABEL_HELP_DRAW_RECT
#define SARIBBONPANELLABEL_HELP_DRAW_RECT(p, rect)                                                                     \
    do {                                                                                                               \
        p.save();                                                                                                      \
        QPen _pen(Qt::DashLine);                                                                                       \
        _pen.setColor(Qt::blue);                                                                                       \
        p.setPen(_pen);                                                                                                \
        p.setBrush(QBrush());                                                                                          \
        p.drawRect(rect);                                                                                              \
        p.restore();                                                                                                   \
    } while (0)
#endif  // SARIBBONPANELLABEL_HELP_DRAW_RECT
#endif
//===============================================================
// SARibbonPanelLabel
//===============================================================

SARibbonPanelLabel::SARibbonPanelLabel(QWidget* parent) : QLabel(parent)
{
}

//===============================================================
// SARibbonPanel::PrivateData
//===============================================================

class SARibbonPanel::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonPanel)
public:
    PrivateData(SARibbonPanel* p);
    // 根据m_panelLayoutMode返回gridLayout应该增加的行数
    int rowadded();
    void createLayout();
    // 获取layout
    SARibbonPanelLayout* panelLayout() const;
    // 返回最后一个添加的action对应的button，前提是最后一个是toolbutton，否则返回nullptr
    SARibbonToolButton* lastAddActionButton();
    // 重置labe的字体，这个主要是为了让panel的标题字体能适应标题高度
    void resetTitleLabelFont();
    // 标题
    QString panelName() const;
    void setPanelName(const QString& title);

public:
    bool m_isCanCustomize { true };                                                    ///< 记录是否可自定义
    SARibbonPanel::PanelLayoutMode m_panelLayoutMode { SARibbonPanel::ThreeRowMode };  ///< panel的布局模式，默认为3行模式ThreeRowMode
    SARibbonPanelOptionButton* m_optionActionButton { nullptr };  ///< 标题栏的y距离
    SARibbonPanelLabel* m_label { nullptr };
};

SARibbonPanel::PrivateData::PrivateData(SARibbonPanel* p) : q_ptr(p)
{
    createLayout();
}

int SARibbonPanel::PrivateData::rowadded()
{
    switch (m_panelLayoutMode) {
    case SARibbonPanel::ThreeRowMode:
        return (2);

    case SARibbonPanel::TwoRowMode:
        return (3);

    default:
        break;
    }
    return (2);
}

void SARibbonPanel::PrivateData::createLayout()
{
    m_label = new SARibbonPanelLabel(q_ptr);
    m_label->setAlignment(Qt::AlignCenter);
    SARibbonPanelLayout* lay = new SARibbonPanelLayout(q_ptr);
    lay->setPanelTitleLabel(m_label);
    lay->setSpacing(2);
    lay->setContentsMargins(2, 2, 2, 2);
}

SARibbonPanelLayout* SARibbonPanel::PrivateData::panelLayout() const
{
    return qobject_cast< SARibbonPanelLayout* >(q_ptr->layout());
}

SARibbonToolButton* SARibbonPanel::PrivateData::lastAddActionButton()
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return (qobject_cast< SARibbonToolButton* >(lay->lastWidget()));
    }
    return (nullptr);
}

/**
 * @brief 重置label的字体
 *
 * @note 此函数必须在布局设置后调用
 */
void SARibbonPanel::PrivateData::resetTitleLabelFont()
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        int h = lay->panelTitleHeight();
        if (h > 1) {
            QFont f = q_ptr->font();
            f.setPixelSize(h * 0.8);
            if (m_label) {
                m_label->setFont(f);
            }
        }
    }
}

QString SARibbonPanel::PrivateData::panelName() const
{
    if (m_label) {
        return m_label->text();
    }
    return (QString());
}

void SARibbonPanel::PrivateData::setPanelName(const QString& title)
{
    if (m_label) {
        m_label->setText(title);
    }
}

//==================================================
// SARibbonPanel
//==================================================

/**
 * @brief Constructs a SARibbonPanel with the given parent / 使用给定的父对象构造SARibbonPanel
 * @param parent The parent widget / 父窗口部件
 */
SARibbonPanel::SARibbonPanel(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonPanel::PrivateData(this))
{
    setPanelLayoutMode(ThreeRowMode);
}

/**
 * @brief Constructs a SARibbonPanel with a name and the given parent / 使用名称和给定的父对象构造SARibbonPanel
 * @param name The panel's display name / 面板的显示名称
 * @param parent The parent widget / 父窗口部件
 */
SARibbonPanel::SARibbonPanel(const QString& name, QWidget* parent)
    : QFrame(parent), d_ptr(new SARibbonPanel::PrivateData(this))
{
    setPanelLayoutMode(ThreeRowMode);
    setPanelName(name);
}

SARibbonPanel::~SARibbonPanel()
{
}

/**
 * @brief Sets the row proportion property for an action / 为一个action设置行占比属性
 *
 * This property determines how much vertical space the corresponding button will occupy within the panel.
 * It should be set before adding the action to the panel.
 *
 * 此属性决定了对应按钮在面板内占据的垂直空间比例。
 * 应在将action添加到面板之前设置。
 *
 * @param action The action to modify / 要修改的action
 * @param rp The row proportion (Large, Medium, Small) / 行占比（大、中、小）
 * @sa addAction, getActionRowProportionProperty
 */
void SARibbonPanel::setActionRowProportionProperty(QAction* action, SARibbonPanelItem::RowProportion rp)
{
    Q_CHECK_PTR(action);
    action->setProperty(SA_ActionPropertyName_RowProportion, static_cast< int >(rp));
}

/**
 * @brief Gets the row proportion property from an action / 从一个action获取行占比属性
 * @param action The action to query / 要查询的action
 * @return The row proportion, defaults to Large if not set / 行占比，如果未设置则默认为Large
 * @sa setActionRowProportionProperty
 */
SARibbonPanelItem::RowProportion SARibbonPanel::getActionRowProportionProperty(QAction* action)
{
    bool isok = false;
    int r     = action->property(SA_ActionPropertyName_RowProportion).toInt(&isok);

    if (isok) {
        return (static_cast< SARibbonPanelItem::RowProportion >(r));
    }
    return (SARibbonPanelItem::Large);
}

/**
 * @brief Sets the ToolButtonPopupMode property for an action / 为一个action设置ToolButtonPopupMode属性
 * @param action The action to modify / 要修改的action
 * @param popMode The popup mode (e.g., InstantPopup, MenuButtonPopup) / 弹出模式（例如，InstantPopup, MenuButtonPopup）
 * @sa getActionToolButtonPopupModeProperty
 */
void SARibbonPanel::setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode)
{
    Q_CHECK_PTR(action);
    action->setProperty(SA_ActionPropertyName_ToolButtonPopupMode, static_cast< int >(popMode));
}

/**
 * @brief Gets the ToolButtonPopupMode property from an action / 从一个action获取ToolButtonPopupMode属性
 * @param action The action to query / 要查询的action
 * @return The popup mode, defaults to InstantPopup if not set / 弹出模式，如果未设置则默认为InstantPopup
 * @sa setActionToolButtonPopupModeProperty
 */
QToolButton::ToolButtonPopupMode SARibbonPanel::getActionToolButtonPopupModeProperty(QAction* action)
{
    bool isok = false;
    int r     = action->property(SA_ActionPropertyName_ToolButtonPopupMode).toInt(&isok);

    if (isok) {
        return (static_cast< QToolButton::ToolButtonPopupMode >(r));
    }
    return (QToolButton::InstantPopup);
}

/**
 * @brief Sets the ToolButtonStyle property for an action / 为一个action设置ToolButtonStyle属性
 * @param action The action to modify / 要修改的action
 * @param buttonStyle The button style (e.g., ToolButtonIconOnly, ToolButtonTextBesideIcon) /
 * 按钮样式（例如，ToolButtonIconOnly, ToolButtonTextBesideIcon）
 * @sa getActionToolButtonStyleProperty
 */
void SARibbonPanel::setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle)
{
    Q_CHECK_PTR(action);
    action->setProperty(SA_ActionPropertyName_ToolButtonStyle, static_cast< int >(buttonStyle));
}

/**
 * @brief Gets the ToolButtonStyle property from an action / 从一个action获取ToolButtonStyle属性
 * @param action The action to query / 要查询的action
 * @return The button style, defaults to ToolButtonIconOnly if not set / 按钮样式，如果未设置则默认为ToolButtonIconOnly
 * @sa setActionToolButtonStyleProperty
 */
Qt::ToolButtonStyle SARibbonPanel::getActionToolButtonStyleProperty(QAction* action)
{
    bool isok = false;
    int r     = action->property(SA_ActionPropertyName_ToolButtonStyle).toInt(&isok);

    if (isok) {
        return (static_cast< Qt::ToolButtonStyle >(r));
    }
    return (Qt::ToolButtonIconOnly);
}

/**
 * @brief Adds an action to the panel / 向面板添加一个action
 *
 * This is the primary method for populating the panel. The button created for this action will use the
 * row proportion and popup mode previously set via the static property functions.
 *
 * 这是填充面板的主要方法。为此action创建的按钮将使用之前通过静态属性函数设置的行占比和弹出模式。
 *
 * @param action The action to add / 要添加的action
 * @param rp The row proportion for this action / 此action的行占比
 * @sa addLargeAction, addMediumAction, addSmallAction
 */
void SARibbonPanel::addAction(QAction* action, SARibbonPanelItem::RowProportion rowProportion)
{
    Q_CHECK_PTR(action);
    setActionRowProportionProperty(action, rowProportion);
    addAction(action);
}

/**
 * @brief Adds an action with a specified popup mode / 添加一个具有指定弹出模式的action
 * @param act The action to add / 要添加的action
 * @param popMode The popup mode for the button / 按钮的弹出模式
 * @param rp The row proportion for this action / 此action的行占比
 */
void SARibbonPanel::addAction(QAction* act,
                              QToolButton::ToolButtonPopupMode popMode,
                              SARibbonPanelItem::RowProportion rowProportion)
{
    Q_CHECK_PTR(act);
    setActionRowProportionProperty(act, rowProportion);
    setActionToolButtonPopupModeProperty(act, popMode);
    addAction(act);
}

/**
 * @brief Adds a large action (occupies full height) / 添加一个大action（占据整个高度）
 * @param action The action to add / 要添加的action
 */
void SARibbonPanel::addLargeAction(QAction* action)
{
    addAction(action, SARibbonPanelItem::Large);
}

/**
 * @brief Adds a medium action / 添加一个中等action
 *
 * In ThreeRowMode, a medium action occupies two rows. In TwoRowMode, it is equivalent to a small action.
 *
 * 在三行模式下，中等action占据两行。在两行模式下，它等同于一个小action。
 *
 * @param action The action to add / 要添加的action
 */
void SARibbonPanel::addMediumAction(QAction* action)
{
    addAction(action, SARibbonPanelItem::Medium);
}

/**
 * @brief Adds a small action / 添加一个小action
 * @param action The action to add / 要添加的action
 */
void SARibbonPanel::addSmallAction(QAction* action)
{
    addAction(action, SARibbonPanelItem::Small);
}

/**
 * @brief Adds a small action with a specified popup mode / 添加一个具有指定弹出模式的小action
 * @param action The action to add / 要添加的action
 * @param popMode The popup mode for the button / 按钮的弹出模式
 */
void SARibbonPanel::addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode)
{
    addAction(action, popMode, SARibbonPanelItem::Small);
}

/**
 * @brief Adds a large action with a specified popup mode / 添加一个具有指定弹出模式的大action
 * @param action The action to add / 要添加的action
 * @param popMode The popup mode for the button / 按钮的弹出模式
 */
void SARibbonPanel::addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode)
{
    addAction(action, popMode, SARibbonPanelItem::Large);
}

/**
 * @brief Adds a medium action with a specified popup mode / 添加一个具有指定弹出模式的中等action
 * @param action The action to add / 要添加的action
 * @param popMode The popup mode for the button / 按钮的弹出模式
 */
void SARibbonPanel::addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode)
{
    addAction(action, popMode, SARibbonPanelItem::Medium);
}

/**
 * @brief Creates and adds a new action / 创建并添加一个新的action
 *
 * This is a convenience function that creates a new `QAction`, sets its properties, and adds it to the panel.
 * The panel will take ownership of the action.
 *
 * 这是一个便捷函数，用于创建一个新的 `QAction`，设置其属性，并将其添加到面板。
 * 面板将拥有此action的所有权。
 *
 * @param text The action's text / action的文字
 * @param icon The action's icon / action的图标
 * @param popMode The button's popup mode / 按钮的弹出模式
 * @param rp The action's row proportion / action的行占比
 * @return The newly created action / 新创建的action
 */
QAction* SARibbonPanel::addAction(const QString& text,
                                  const QIcon& icon,
                                  QToolButton::ToolButtonPopupMode popMode,
                                  SARibbonPanelItem::RowProportion rowProportion)
{
    QAction* action = new QAction(icon, text, this);
    addAction(action, popMode, rowProportion);
    return (action);
}

/**
 * @brief Adds a menu to the panel / 向面板添加一个菜单
 * @param menu The menu to add / 要添加的菜单
 * @param rp The row proportion for the menu button / 菜单按钮的行占比
 * @param popMode The popup mode, defaults to InstantPopup / 弹出模式，默认为InstantPopup
 * @note 本函数会修改 menu->menuAction() 的部分属性（icon/text/objectName），如需保留自定义值，请提前设置。
 *
 */
void SARibbonPanel::addMenu(QMenu* menu, SARibbonPanelItem::RowProportion rowProportion, QToolButton::ToolButtonPopupMode popMode)
{
    Q_CHECK_PTR(menu);
    QAction* action = menu->menuAction();
    // 仅当未设置时才填充默认值，避免覆盖用户自定义
    if (action->icon().isNull()) {
        action->setIcon(menu->icon());
    }
    if (action->text().isEmpty()) {
        action->setText(menu->title());
    }
    if (action->objectName().isEmpty()) {
        action->setObjectName("action." + menu->objectName());
    }
    addAction(action, popMode, rowProportion);
}

/**
 * @brief Adds a menu as a large button / 以大按钮形式添加一个菜单
 * @param menu The menu to add / 要添加的菜单
 * @param popMode The popup mode / 弹出模式
 */
void SARibbonPanel::addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode)
{
    addMenu(menu, SARibbonPanelItem::Large, popMode);
}

/**
 * @brief Adds a menu as a medium button / 以中按钮形式添加一个菜单
 * @param menu The menu to add / 要添加的菜单
 * @param popMode The popup mode / 弹出模式
 */
void SARibbonPanel::addMediumMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode)
{
    addMenu(menu, SARibbonPanelItem::Medium, popMode);
}

/**
 * @brief Adds a menu as a small button / 以小按钮形式添加一个菜单
 * @param menu The menu to add / 要添加的菜单
 * @param popMode The popup mode / 弹出模式
 */
void SARibbonPanel::addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode)
{
    addMenu(menu, SARibbonPanelItem::Small, popMode);
}

/**
 * @brief Adds a custom widget to the panel / 向面板添加一个自定义窗口部件
 *
 * The panel will create a `QWidgetAction` to manage the widget. The panel does not take ownership of the widget;
 * its lifetime is managed by its parent or the caller.
 *
 * 面板将创建一个 `QWidgetAction` 来管理此窗口部件。面板不拥有此窗口部件的所有权；
 * 其生命周期由其父对象或调用者管理。
 *
 * @param w The widget to add / 要添加的窗口部件
 * @param rp The row proportion for the widget / 窗口部件的行占比
 * @return The created QWidgetAction / 创建的QWidgetAction
 */
QAction* SARibbonPanel::addWidget(QWidget* w, SARibbonPanelItem::RowProportion rowProportion)
{
    QWidgetAction* action = new QWidgetAction(this);

    action->setDefaultWidget(w);

    action->setEnabled(w->isEnabled());
    // 建立 Action -> Widget 的单向同步
    connect(action, &QWidgetAction::changed, this, [ w, action ]() {
        if (w) {
            w->setEnabled(action->isEnabled());
        }
    });
    action->setIcon(w->windowIcon());
    action->setText(w->windowTitle());
    action->setObjectName("action." + w->objectName());
    w->setAttribute(Qt::WA_Hover);
    setActionRowProportionProperty(action, rowProportion);
    addAction(action);
    return (action);
}

/**
 * @brief Adds a widget as a small button / 以小按钮形式添加一个窗口部件
 * @param w The widget to add / 要添加的窗口部件
 * @return The created QWidgetAction / 创建的QWidgetAction
 */
QAction* SARibbonPanel::addSmallWidget(QWidget* w)
{
    return (addWidget(w, SARibbonPanelItem::Small));
}

/**
 * @brief Adds a widget as a medium button / 以中等按钮形式添加一个窗口部件
 * @param w The widget to add / 要添加的窗口部件
 * @return The created QWidgetAction / 创建的QWidgetAction
 */
QAction* SARibbonPanel::addMediumWidget(QWidget* w)
{
    return (addWidget(w, SARibbonPanelItem::Medium));
}

/**
 * @brief Adds a widget as a large button / 以大按钮形式添加一个窗口部件
 * @param w The widget to add / 要添加的窗口部件
 * @return The created QWidgetAction / 创建的QWidgetAction
 */
QAction* SARibbonPanel::addLargeWidget(QWidget* w)
{
    return (addWidget(w, SARibbonPanelItem::Large));
}

/**
 * @brief Adds a gallery widget to the panel / 向面板添加一个画廊窗口部件
 *
 * A gallery is a specialized widget for displaying a collection of items. If `expanding` is true,
 * the panel's horizontal size policy will be set to `Expanding`.
 *
 * 画廊是一个用于显示项目集合的专用窗口部件。如果 `expanding` 为true，
 * 面板的水平尺寸策略将被设置为 `Expanding`。
 *
 * @param expanding If true, sets the panel's size policy to Expanding / 如果为true，将面板的尺寸策略设置为Expanding
 * @return The created SARibbonGallery / 创建的SARibbonGallery
 */
SARibbonGallery* SARibbonPanel::addGallery(bool expanding)
{
    SARibbonGallery* gallery = RibbonSubElementFactory->createRibbonGallery(this);

    addWidget(gallery, SARibbonPanelItem::Large);
    if (expanding) {
        setExpanding(expanding);
    }
    return (gallery);
}

/**
 * @brief Adds a separator / 添加一个分隔符
 * @return The created separator action / 创建的分隔符action
 */
QAction* SARibbonPanel::addSeparator()
{
    QAction* action = new QAction(this);

    action->setSeparator(true);
    setActionRowProportionProperty(action, SARibbonPanelItem::Large);
    addAction(action);
    return (action);
}

/**
 * @brief Retrieves the SARibbonToolButton associated with an action / 获取与action关联的SARibbonToolButton
 * @param action The action to query / 要查询的action
 * @return The associated tool button, or nullptr if not found / 关联的工具按钮，如果未找到则返回nullptr
 */
SARibbonToolButton* SARibbonPanel::actionToRibbonToolButton(QAction* action)
{
    for (auto obj : qAsConst(children())) {
        if (obj->isWidgetType()) {
            if (SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(obj)) {
                if (btn->defaultAction() == action) {
                    return btn;
                }
            }
        }
    }
    return (nullptr);
}

/**
 * @brief Returns a list of all SARibbonToolButton children / 返回所有SARibbonToolButton子对象的列表
 * @return A list of pointers to SARibbonToolButton / SARibbonToolButton指针的列表
 */
QList< SARibbonToolButton* > SARibbonPanel::ribbonToolButtons() const
{
    const QObjectList& objs = children();
    QList< SARibbonToolButton* > res;

    for (QObject* o : objs) {
        SARibbonToolButton* b = qobject_cast< SARibbonToolButton* >(o);
        if (b) {
            res.append(b);
        }
    }
    return (res);
}

/**
 * @brief Sets the panel's layout mode / 设置面板的布局模式
 * @param mode The new layout mode (ThreeRowMode or TwoRowMode) / 新的布局模式（三行模式或两行模式）
 * @sa panelLayoutMode
 */
void SARibbonPanel::setPanelLayoutMode(SARibbonPanel::PanelLayoutMode mode)
{
    if (d_ptr->m_panelLayoutMode == mode) {
        return;
    }
    d_ptr->m_panelLayoutMode = mode;
    updateItemGeometry();
}

/**
 * @brief Gets the current layout mode / 获取当前的布局模式
 * @return The current layout mode / 当前的布局模式
 * @sa setPanelLayoutMode
 */
SARibbonPanel::PanelLayoutMode SARibbonPanel::panelLayoutMode() const
{
    return (d_ptr->m_panelLayoutMode);
}

/**
 * @brief Sets the option action for the panel / 为面板设置选项action
 *
 * The option action is typically displayed as a small button in the panel's title area.
 * Pass `nullptr` to remove the current option action.
 *
 * 选项action通常显示在面板标题区域的一个小按钮中。
 * 传入 `nullptr` 以移除当前的选项action。
 *
 * @param action The option action, or nullptr to remove / 选项action，或传入nullptr以移除
 * @sa isHaveOptionAction
 */
void SARibbonPanel::setOptionAction(QAction* action)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->setOptionAction(action);
    }
}

/**
 * @brief Checks if an option action is set / 检查是否设置了选项action
 * @return true if an option action exists; otherwise false / 如果存在选项action则返回true；否则返回false
 * @sa setOptionAction
 */
bool SARibbonPanel::isHaveOptionAction() const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->isHaveOptionAction();
    }
    return false;
}

QSize SARibbonPanel::sizeHint() const
{
    int shWidth  = 500;
    int shHeight = 100;
    if (QLayout* lay = layout()) {
        QSize laySize = layout()->sizeHint();
        shWidth       = laySize.width();
        shHeight      = laySize.height();
    }
    return QSize(shWidth, shHeight);
}

QSize SARibbonPanel::minimumSizeHint() const
{
    return (layout()->minimumSize());
}

/**
 * @brief Sets the icon size for all tool buttons in the panel / 设置面板内所有工具按钮的图标尺寸
 *
 * This sets the default icon size for newly created buttons and updates the icon size for existing buttons.
 *
 * 此函数设置新创建按钮的默认图标尺寸，并更新现有按钮的图标尺寸。
 *
 * @param smallSize The new icon size / 新的图标尺寸
 * @sa toolButtonIconSize
 */
void SARibbonPanel::setToolButtonIconSize(const QSize& smallSize, const QSize& largeSize)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->setToolButtonIconSize(smallSize, largeSize);
    }
    // 对已经管理的QToolButton设置为iconsize
    iterateButton([ smallSize, largeSize ](SARibbonToolButton* t) -> bool {
        t->setIconSize(smallSize);
        t->setLargeIconSize(largeSize);
        return true;
    });
}

/**
 * @brief Gets the current default icon size for tool buttons / 获取工具按钮当前的默认图标尺寸
 * @return The current icon size / 当前的图标尺寸
 * @sa setToolButtonIconSize
 */
QPair< QSize, QSize > SARibbonPanel::toolButtonIconSize() const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->toolButtonIconSize();
    }
    return QPair< QSize, QSize >();
}

void SARibbonPanel::setLargeIconSize(const QSize& largeSize)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->setLargeIconSize(largeSize);
    }
    // 对已经管理的QToolButton设置为iconsize
    iterateButton([ largeSize ](SARibbonToolButton* t) -> bool {
        t->setLargeIconSize(largeSize);
        return true;
    });
}

QSize SARibbonPanel::largeIconSize() const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->largeIconSize();
    }
    return QSize();
}

void SARibbonPanel::setSmallIconSize(const QSize& smallSize)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->setSmallIconSize(smallSize);
    }
    // 对已经管理的QToolButton设置为iconsize
    iterateButton([ smallSize ](SARibbonToolButton* t) -> bool {
        t->setSmallIconSize(smallSize);
        return true;
    });
}

QSize SARibbonPanel::smallIconSize() const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->smallIconSize();
    }
    return QSize();
}

/**
 * @brief Iterates over all SARibbonToolButton children / 遍历所有SARibbonToolButton子对象
 *
 * Executes the provided function pointer for each `SARibbonToolButton`. If the function returns `false`,
 * iteration stops immediately.
 *
 * 对每个 `SARibbonToolButton` 执行提供的函数指针。如果函数返回 `false`，
 * 迭代将立即停止。
 *
 * Example:
 * @code
 * myPanel->iterateButton([](SARibbonToolButton* btn) -> bool {
 *     btn->setToolTip("Visited");
 *     return true; // Continue to next button/继续下一个按钮
 * });
 * @endcode
 *
 * @param fp A function pointer that takes a SARibbonToolButton* and returns a bool / 一个接受SARibbonToolButton*并返回bool的函数指针
 * @return true if all buttons were iterated; false if iteration was stopped early / 如果遍历了所有按钮则返回true；如果提前停止则返回false
 */
bool SARibbonPanel::iterateButton(SARibbonPanel::FpRibbonToolButtonIterate fp) const
{
    const QObjectList& ols = children();
    for (QObject* obj : ols) {
        if (SARibbonToolButton* t = qobject_cast< SARibbonToolButton* >(obj)) {
            if (!fp(t)) {
                return false;
            }
        }
    }
    return true;
}

/**
 * @brief Sets the panel to expanding mode / 将面板设置为扩展模式
 *
 * In expanding mode, the panel will try to occupy all available horizontal space in its parent layout.
 *
 * 在扩展模式下，面板将尝试占据其父布局中所有可用的水平空间。
 *
 * @param isExpanding If true, sets the horizontal size policy to Expanding / 如果为true，将水平尺寸策略设置为Expanding
 * @sa isExpanding
 */
void SARibbonPanel::setExpanding(bool isExpanding)
{
    setSizePolicy(isExpanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed);
}

/**
 * @brief Checks if the panel is in expanding mode / 检查面板是否处于扩展模式
 * @return true if the horizontal size policy is Expanding; otherwise false / 如果水平尺寸策略为Expanding则返回true；否则返回false
 * @sa setExpanding
 */
bool SARibbonPanel::isExpanding() const
{
    QSizePolicy sp = sizePolicy();

    return (sp.horizontalPolicy() == QSizePolicy::Expanding);
}

/**
 * @brief Sets the height of the panel's title / 设置面板标题的高度
 *
 * This function is typically called by `SARibbonBar` and is protected to prevent direct user manipulation.
 *
 * 此函数通常由 `SARibbonBar` 调用，为防止用户直接操作而设为protected。
 *
 * @param h The new title height / 新的标题高度
 * @sa SARibbonBar::setPanelTitleHeight
 */
void SARibbonPanel::setTitleHeight(int h)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->setPanelTitleHeight(h);
    }
    d_ptr->resetTitleLabelFont();
}

/**
 * @brief Gets the height of the panel's title / 获取面板标题的高度
 * @return The current title height / 当前的标题高度
 */
int SARibbonPanel::titleHeight() const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->panelTitleHeight();
    }
    return -1;
}

/**
 * @brief Checks if the panel's title is visible / 检查面板标题是否可见
 * @return true if the title is visible; otherwise false / 如果标题可见则返回true；否则返回false
 */
bool SARibbonPanel::isEnableShowTitle() const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->isEnableShowPanelTitle();
    }
    return false;
}

/**
 * @brief Sets whether the panel's title is visible / 设置面板标题是否可见
 *
 * This function is typically called by `SARibbonBar` and is protected to prevent direct user manipulation.
 *
 * 此函数通常由 `SARibbonBar` 调用，为防止用户直接操作而设为protected。
 *
 * @param on If true, the title is visible / 如果为true，则标题可见
 * @sa SARibbonBar::setEnableShowPanelTitle
 */
void SARibbonPanel::setEnableShowTitle(bool on)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return lay->setEnableShowPanelTitle(on);
    }
}

/**
 * @brief Gets the index of an action within the panel / 获取一个action在面板内的索引
 * @param act The action to find / 要查找的action
 * @return The index of the action, or -1 if not found / action的索引，如果未找到则返回-1
 */
int SARibbonPanel::actionIndex(QAction* act) const
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        return (lay->indexByAction(act));
    }
    return (-1);
}

/**
 * @brief Moves an action to a new position / 将一个action移动到新位置
 * @param from The current index of the action / action的当前索引
 * @param to The new index for the action / action的新索引
 */
void SARibbonPanel::moveAction(int from, int to)
{
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->move(from, to);
        lay->activate();
    }
}

/**
 * @brief Checks if customization is allowed / 检查是否允许自定义
 * @return true if customization is allowed; otherwise false / 如果允许自定义则返回true；否则返回false
 * @sa setCanCustomize
 */
bool SARibbonPanel::isCanCustomize() const
{
    return (d_ptr->m_isCanCustomize);
}

/**
 * @brief Sets whether customization is allowed / 设置是否允许自定义
 * @param b If true, customization is allowed / 如果为true，则允许自定义
 * @sa isCanCustomize
 */
void SARibbonPanel::setCanCustomize(bool b)
{
    d_ptr->m_isCanCustomize = b;
}

/**
 * @brief Gets the panel's name / 获取面板的名称
 * @return The panel's name / 面板的名称
 * @sa setPanelName
 */
QString SARibbonPanel::panelName() const
{
    return (d_ptr->panelName());
}

/**
 * @brief Sets the panel's name / 设置面板的名称
 *
 * Setting the name will also emit the `panelNameChanged` signal.
 *
 * 设置名称还会发出 `panelNameChanged` 信号。
 *
 * @param title The new name for the panel / 面板的新名称
 * @sa panelName
 */
void SARibbonPanel::setPanelName(const QString& title)
{
    QString oldName = panelName();
    if (oldName != title) {
        d_ptr->setPanelName(title);
        Q_EMIT panelNameChanged(title);
    }
}

/**
 * @brief Gets the height of the large buttons in this panel / 获取此面板中大按钮的高度
 * @return The height of large buttons / 大按钮的高度
 */
int SARibbonPanel::largeButtonHeight() const
{
    const QMargins& mag = contentsMargins();
    return height() - mag.top() - mag.bottom() - titleHeight();
}

/**
 * @brief Gets the panel's layout object / 获取面板的布局对象
 * @return A pointer to the SARibbonPanelLayout / 指向SARibbonPanelLayout的指针
 */
SARibbonPanelLayout* SARibbonPanel::panelLayout() const
{
    return d_ptr->panelLayout();
}

/**
 * @brief Updates the geometry of all items in the panel / 更新面板内所有项目的几何形状
 *
 * This function forces a recalculation of the layout and size hints for all buttons and items.
 *
 * 此函数强制重新计算所有按钮和项目的布局及尺寸提示。
 */
void SARibbonPanel::updateItemGeometry()
{
#if SARIBBONPANEL_DEBUG_PRINT
    qDebug() << "SARibbonPanel updateItemGeometry,panelName=" << panelName();
#endif
    // 此函数需要添加，否则SARibbonBar::setEnableWordWrap无法刷新按钮
    // resetToolButtonSize();
    if (SARibbonPanelLayout* lay = panelLayout()) {
        lay->updateGeomArray();
    }
}

/**
 * @brief Gets the parent SARibbonCategory / 获取父SARibbonCategory
 * @return The parent category, or nullptr if not found / 父category，如果未找到则返回nullptr
 */
SARibbonCategory* SARibbonPanel::category() const
{
    return qobject_cast< SARibbonCategory* >(parent());
}

/**
 * @brief Gets the top-level SARibbonBar / 获取顶层的SARibbonBar
 * @return The ribbon bar, or nullptr if not found / Ribbon栏，如果未找到则返回nullptr
 */
SARibbonBar* SARibbonPanel::ribbonBar() const
{
    if (SARibbonCategory* c = category()) {
        return c->ribbonBar();
    }
    return nullptr;
}

/**
 * @brief Sets the spacing between elements in the panel / 设置面板内元素之间的间距
 *
 * This function is typically called by `SARibbonBar` and is protected to prevent direct user manipulation.
 *
 * 此函数通常由 `SARibbonBar` 调用，为防止用户直接操作而设为protected。
 *
 * @param n The new spacing value / 新的间距值
 * @sa SARibbonBar::setPanelSpacing
 */
void SARibbonPanel::setSpacing(int n)
{
    if (auto lay = layout()) {
        lay->setSpacing(n);
    }
}

/**
 * @brief Gets the spacing between elements in the panel / 获取面板内元素之间的间距
 * @return The current spacing value / 当前的间距值
 * @sa setSpacing
 */
int SARibbonPanel::spacing() const
{
    if (auto lay = layout()) {
        return lay->spacing();
    }
    return 0;
}

/**
 * @brief Gets the panel's recommended height / 获取面板的推荐高度
 *
 * This is a static helper function used internally for layout calculations.
 *
 * 这是一个用于内部布局计算的静态辅助函数。
 *
 * @param fm The font metrics to use for calculation / 用于计算的字体度量
 * @param layMode The layout mode / 布局模式
 * @param panelTitleHeight The height of the panel's title / 面板标题的高度
 * @return The recommended height / 推荐的高度
 */
int SARibbonPanel::panelHeightHint(const QFontMetrics& fm, PanelLayoutMode layMode, int panelTitleHeight)
{
    int textH = fm.lineSpacing();  // 这里用linespace，因为在换行的情况下，行距是不可忽略的，ribbon的大按钮默认是2行
    switch (layMode) {
    case SARibbonPanel::ThreeRowMode: {
        // 5.5=（3*1.6+1） （三行）,1是给paneltitle预留的
        return textH * 4.8 + panelTitleHeight;
    } break;
    case SARibbonPanel::TwoRowMode: {
        // 3=2*1.6
        return textH * 3.2 + panelTitleHeight;
    } break;
    default: {
        qWarning() << "unknow SARibbonPanel::PanelLayoutMode:" << layMode;
    }
    }
    return (textH * 4.8 + panelTitleHeight);
}

/**
 * @brief 重置按钮的尺寸，在布局改变后（尤其高度变更），按钮的尺寸需要手动变更
 */
void SARibbonPanel::resetToolButtonSize()
{
    QList< SARibbonToolButton* > btns = ribbonToolButtons();

    for (SARibbonToolButton* b : qAsConst(btns)) {
        if ((nullptr == b)) {
            continue;
        }
        b->invalidateSizeHint();
    }
}

bool SARibbonPanel::isTwoRow() const
{
    return (TwoRowMode == panelLayoutMode());
}

/**
 * @brief Handles action events (add, remove, change) / 处理action事件（添加、移除、更改）
 *
 * This function is called by Qt when actions are added, removed, or changed. It ensures the panel's
 * layout is updated accordingly.
 *
 * 当action被添加、移除或更改时，Qt会调用此函数。它确保面板的布局相应更新。
 *
 * @param e The action event / action事件
 */
void SARibbonPanel::actionEvent(QActionEvent* e)
{
    QAction* action             = e->action();
    QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action);

    switch (e->type()) {
    case QEvent::ActionAdded: {
        SARibbonPanelLayout* lay = panelLayout();
        if (nullptr != widgetAction) {
            if (widgetAction->parent() != this) {
                widgetAction->setParent(this);
            }
        }
        int index = lay->count();
        if (e->before()) {
            // 说明是插入
            index = lay->indexByAction(e->before());
            if (-1 == index) {
                index = lay->count();  // 找不到的时候就插入到最后
            }
        }
        lay->insertAction(index, action, getActionRowProportionProperty(action));
        // 通知父布局这个控件的尺寸提示(sizeHint())可能已改变
        updateGeometry();
    } break;

    case QEvent::ActionChanged: {
        // 让布局重新绘制
        // 通知父布局这个控件的尺寸提示(sizeHint())可能已改变
        updateGeometry();
        // 只处理 QWidgetAction 的情况
        if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(e->action())) {
            // 安全获取关联控件
            if (QWidget* w = widgetAction->defaultWidget()) {
                w->setEnabled(widgetAction->isEnabled());
            }
        }
    } break;

    case QEvent::ActionRemoved: {
        SARibbonPanelLayout* lay = panelLayout();
        action->disconnect(this);
        int index = lay->indexByAction(action);
        if (index != -1) {
            QLayoutItem* item = lay->takeAt(index);
            delete item;
        }
        updateGeometry();
    } break;

    default:
        break;
    }
}

/**
 * @brief Handles change events (e.g., font change) / 处理更改事件（例如，字体更改）
 *
 * This function is called when properties like the font change. It propagates the change to child
 * widgets and invalidates the layout.
 *
 * 当字体等属性更改时会调用此函数。它将更改传播到子窗口部件并使布局失效。
 *
 * @param e The change event / 更改事件
 */
void SARibbonPanel::changeEvent(QEvent* e)
{
    if (nullptr == e) {
        return;
    }
    if (e->type() == QEvent::FontChange) {
        if (d_ptr->m_label) {
            d_ptr->m_label->setFont(font());
        }
        if (QLayout* lay = layout()) {
            lay->invalidate();
        }
        d_ptr->resetTitleLabelFont();
    }
    QWidget::changeEvent(e);
}

/**
 * @brief 获取布局item
 * @return
 */
const QList< SARibbonPanelItem* >& SARibbonPanel::ribbonPanelItem() const
{
    return (panelLayout()->mItems);
}

/**
 * @brief 获取最近添加的按钮
 * @return 刚才添加的按钮的指针
 */
SARibbonToolButton* SARibbonPanel::lastAddActionButton()
{
    return d_ptr->lastAddActionButton();
}

/**
 * @brief Gets the title label widget / 获取标题标签窗口部件
 * @return A pointer to the SARibbonPanelLabel / 指向SARibbonPanelLabel的指针
 */
SARibbonPanelLabel* SARibbonPanel::titleLabel() const
{
    return d_ptr->m_label;
}

/**
 * @brief 设置panel的按钮文字允许换行
 * @param on
 */
void SARibbonPanel::setEnableWordWrap(bool on)
{
    if (SARibbonPanelLayout* lay = qobject_cast< SARibbonPanelLayout* >(layout())) {
        // 此函数会自动设置所有按钮的换行状态
        lay->setEnableWordWrap(on);
        updateGeometry();
    }
}

/**
 * @brief 判断panel的文字是否允许换行
 * @return
 */
bool SARibbonPanel::isEnableWordWrap() const
{
    if (SARibbonPanelLayout* lay = qobject_cast< SARibbonPanelLayout* >(layout())) {
        return lay->isEnableWordWrap();
    }
    return false;
}

/**
 * @brief 设置按钮最大宽高比，这个系数决定按钮的最大宽度
 *
 * 按钮的最大宽度为按钮高度*此系数，例如按钮高度为h，那么按钮最大宽度maxw=h*buttonMaximumAspectRatio
 * 如果在此宽度下文字还无法完全显示，那么按钮将不会继续横向扩展，将使用...替代未完全显示的文字
 *
 * @see buttonMaximumAspectRatio
 *
 * @note 用户不应该调用@ref SARibbonPanel::setButtonMaximumAspectRatio 来设置，
 * 而是调用@ref SARibbonBar::setButtonMaximumAspectRatio 设置宽高比
 */
void SARibbonPanel::setButtonMaximumAspectRatio(qreal fac)
{
    if (SARibbonPanelLayout* lay = qobject_cast< SARibbonPanelLayout* >(layout())) {
        // 此函数会自动设置所有按钮的换行状态
        lay->setButtonMaximumAspectRatio(fac);
    }
}

/**
 * @brief 按钮最大宽高比，这个系数决定按钮的最大宽度
 * @return 按钮最大宽高比
 * @see setButtonMaximumAspectRatio
 */
qreal SARibbonPanel::buttonMaximumAspectRatio() const
{
    if (SARibbonPanelLayout* lay = qobject_cast< SARibbonPanelLayout* >(layout())) {
        return lay->buttonMaximumAspectRatio();
    }
    return 1.4;
}

/*** End of inlined file: SARibbonPanel.cpp ***/

/*** Start of inlined file: SARibbonCategory.cpp ***/
#include <QList>
#include <QResizeEvent>
#include <QPainter>
#include <QLinearGradient>
#include <QApplication>
#include <QHBoxLayout>
#include <QList>
#include <QMap>
#include <QResizeEvent>

#ifndef SARIBBONCATEGORY_DEBUG_PRINT
#define SARIBBONCATEGORY_DEBUG_PRINT 0
#endif
#if SARIBBONCATEGORY_DEBUG_PRINT
#include <QDebug>
#endif

/**
 * @brief SARibbonCategory::PrivateData
 */
class SARibbonCategory::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonCategory)
public:
    PrivateData(SARibbonCategory* p);

    SARibbonPanel* addPanel(const QString& title);
    SARibbonPanel* insertPanel(const QString& title, int index);
    void addPanel(SARibbonPanel* panel);
    void insertPanel(int index, SARibbonPanel* panel);

    // 把panel从Category中移除，不会销毁，此时panel的所有权归还操作者
    bool takePanel(SARibbonPanel* panel);

    // 移除Panel，Category会直接回收SARibbonPanel内存
    bool removePanel(SARibbonPanel* panel);
    SARibbonCategory* ribbonCategory();
    const SARibbonCategory* ribbonCategory() const;

    // 返回所有的Panel
    QList< SARibbonPanel* > panelList();

    // 更新item的布局,此函数会调用doItemLayout
    void updateItemGeometry();

    void doWheelEvent(QWheelEvent* event);
    // 初始化
    void init(SARibbonCategory* c);

public:
    bool enableShowPanelTitle { true };             ///< 是否运行panel的标题栏显示
    int panelTitleHeight { 15 };                    ///< panel的标题栏默认高度
    bool isContextCategory { false };               ///< 标记是否是上下文标签
    bool isCanCustomize { true };                   ///< 标记是否可以自定义
    int panelSpacing { 0 };                         ///< panel的spacing
    bool isUseAnimating { true };                   ///< 默认使用动画滚动
    bool enableWordWrap { true };                   ///< 是否文字换行
    QSize panelSmallToolButtonIconSize { 20, 20 };  ///< 记录panel的默认小图标大小
    QSize panelLargeToolButtonIconSize { 32, 32 };  ///< 记录panel的默认大图标大小
    int wheelScrollStep { 400 };                    ///< 默认滚轮滚动步长
    qreal buttonMaximumAspectRatio { 1.4 };         ///< 按钮最大宽高比，这个系数决定按钮的最大宽度
    SARibbonPanel::PanelLayoutMode defaultPanelLayoutMode { SARibbonPanel::ThreeRowMode };
};
SARibbonCategory::PrivateData::PrivateData(SARibbonCategory* p) : q_ptr(p)
{
}

SARibbonPanel* SARibbonCategory::PrivateData::addPanel(const QString& title)
{
    if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) {
        SARibbonPanel* p = insertPanel(title, lay->panelCount());
        return p;
    }
    return nullptr;
}

SARibbonPanel* SARibbonCategory::PrivateData::insertPanel(const QString& title, int index)
{
    SARibbonPanel* panel = RibbonSubElementFactory->createRibbonPanel(ribbonCategory());

    panel->setPanelName(title);
    panel->setObjectName(title);
    insertPanel(index, panel);
    return (panel);
}

void SARibbonCategory::PrivateData::addPanel(SARibbonPanel* panel)
{
    if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) {
        insertPanel(lay->panelCount(), panel);
    }
}

/**
 * @brief 插入panel到layout
 *
 * 所有的添加操作最终会调用此函数
 * @param index
 * @param panel
 */
void SARibbonCategory::PrivateData::insertPanel(int index, SARibbonPanel* panel)
{
    if (nullptr == panel) {
        return;
    }
    SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout());
    if (nullptr == lay) {
        return;
    }
    if (panel->parentWidget() != q_ptr) {
        panel->setParent(q_ptr);
    }
    // 同步状态
    panel->setEnableShowTitle(this->enableShowPanelTitle);
    panel->setTitleHeight(this->panelTitleHeight);
    panel->setPanelLayoutMode(this->defaultPanelLayoutMode);
    panel->setSpacing(this->panelSpacing);
    panel->setToolButtonIconSize(this->panelSmallToolButtonIconSize, this->panelLargeToolButtonIconSize);
    panel->setEnableWordWrap(this->enableWordWrap);

    index = qMax(0, index);
    index = qMin(lay->panelCount(), index);
    lay->insertPanel(index, panel);
    panel->setVisible(true);

    QObject::connect(panel, &SARibbonPanel::actionTriggered, ribbonCategory(), &SARibbonCategory::actionTriggered);
    q_ptr->updateGeometry();  // 通知父布局这个控件的尺寸提示(sizeHint())可能已改变
}

bool SARibbonCategory::PrivateData::takePanel(SARibbonPanel* panel)
{
    SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout());
    if (nullptr == lay) {
        return false;
    }
    bool res = lay->takePanel(panel);
    q_ptr->updateGeometry();  // 通知父布局这个控件的尺寸提示(sizeHint())可能已改变
    return res;
}

bool SARibbonCategory::PrivateData::removePanel(SARibbonPanel* panel)
{
    if (takePanel(panel)) {
        panel->hide();
        panel->deleteLater();
        return (true);
    }
    return (false);
}

QList< SARibbonPanel* > SARibbonCategory::PrivateData::panelList()
{
    if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) {
        return lay->panelList();
    }
    return QList< SARibbonPanel* >();
}

SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory()
{
    return (q_ptr);
}

const SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() const
{
    return (q_ptr);
}

void SARibbonCategory::PrivateData::updateItemGeometry()
{
#if SARIBBONCATEGORY_DEBUG_PRINT
    qDebug() << "SARibbonCategory::PrivateData::updateItemGeometry,categoryName=" << q_ptr->categoryName();
#endif
    SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout());
    if (!lay) {
        return;
    }
    const QList< SARibbonPanel* > panels = lay->panelList();
    for (auto panel : panels) {
        panel->updateItemGeometry();
    }
    lay->updateGeometryArr();
    return;
}

void SARibbonCategory::PrivateData::doWheelEvent(QWheelEvent* event)
{
    SARibbonCategoryLayout* lay = q_ptr->categoryLayout();
    if (nullptr == lay) {
        return;
    }

    // 如果动画正在进行，忽略新的事件
    if (isUseAnimating && lay->isAnimatingScroll()) {
        event->ignore();
        return;
    }

    QSize contentSize = lay->categoryContentSize();
    int totalWidth    = lay->categoryTotalWidth();

    if (totalWidth > contentSize.width()) {
        int scrollStep = wheelScrollStep;

        // 根据滚轮方向确定滚动方向
        QPoint numPixels  = event->pixelDelta();
        QPoint numDegrees = event->angleDelta() / 8;

        if (!numPixels.isNull()) {
            scrollStep = (numPixels.y() < 0) ? -scrollStep : scrollStep;
        } else if (!numDegrees.isNull()) {
            scrollStep = (numDegrees.y() < 0) ? -scrollStep : scrollStep;
        }

        // 动态调整步长 - 滚动越快步长越大
        const int absDelta = qMax(qAbs(numPixels.y()), qAbs(numDegrees.y()));
        if (absDelta > 60) {
            scrollStep *= 2;
        } else if (absDelta < 20) {
            scrollStep /= 2;
        }

        // 根据设置选择滚动方式
        if (isUseAnimating) {
            lay->scrollByAnimate(scrollStep);
        } else {
            lay->scroll(scrollStep);
        }
    } else {
        event->ignore();
        if (lay->isScrolled()) {
            // 根据设置选择复位方式
            if (isUseAnimating) {
                lay->scrollToByAnimate(0);
            } else {
                lay->scroll(0);
            }
        }
    }
}

void SARibbonCategory::PrivateData::init(SARibbonCategory* c)
{
    c->setLayout(new SARibbonCategoryLayout(c));
    c->connect(c, &SARibbonCategory::windowTitleChanged, c, &SARibbonCategory::categoryNameChanged);
}

//----------------------------------------------------
// SARibbonCategory
//----------------------------------------------------

SARibbonCategory::SARibbonCategory(QWidget* p) : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this))
{
    d_ptr->init(this);
}

/**
 * @brief 带名称的构造函数
 * @param name Category名称
 * @param p 父级控件指针
 */
SARibbonCategory::SARibbonCategory(const QString& name, QWidget* p)
    : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this))
{
    setCategoryName(name);
    d_ptr->init(this);
}

SARibbonCategory::~SARibbonCategory()
{
}

/**
 * @brief Get the category name/获取Category名称
 * @return Current category name (i.e., windowTitle)/当前Category的名称（即windowTitle）
 */
QString SARibbonCategory::categoryName() const
{
    return (windowTitle());
}

/**
 * @brief Set the category name/设置Category名称
 * @param title New name (equivalent to setWindowTitle)/新名称（等价于setWindowTitle）
 */
void SARibbonCategory::setCategoryName(const QString& title)
{
    setWindowTitle(title);
}

/**
 * @brief Get the panel layout mode/获取面板布局模式
 * @return Current layout mode for all panels/当前所有面板的布局模式
 */
SARibbonPanel::PanelLayoutMode SARibbonCategory::panelLayoutMode() const
{
    return (d_ptr->defaultPanelLayoutMode);
}

/**
 * @brief Set the panel layout mode/设置面板布局模式
 *
 *  * When @ref SARibbonBar calls the @ref SARibbonBar::setRibbonStyle function, this function will be called for all
 * SARibbonCategory objects to set the new SARibbonPanel::PanelLayoutMode
 *
 * 在@ref SARibbonBar 调用@ref SARibbonBar::setRibbonStyle 函数时，会对所有的SARibbonCategory调用此函数
 * 把新的SARibbonPanel::PanelLayoutMode设置进去
 * @param m
 */
void SARibbonCategory::setPanelLayoutMode(SARibbonPanel::PanelLayoutMode m)
{
    d_ptr->defaultPanelLayoutMode = m;
    iteratePanel([ m ](SARibbonPanel* p) -> bool {
        p->setPanelLayoutMode(m);
        return true;
    });
    updateItemGeometry();
}

/**
 * @brief Add a panel (panel)/添加面板(panel)
 *
 * @note The ownership of the panel (panel) is managed by SARibbonCategory, please do not destroy it
 * externally/面板(panel)的所有权由SARibbonCategory来管理，请不要在外部对其进行销毁
 *
 * @param title Panel (panel) title, which will be displayed below the panel (panel) in the three-row mode of
 * office/wps / 面板(panel)的标题，在office/wps的三行模式下会显示在面板(panel)的下方
 *
 * @return Returns a pointer to the generated @ref SARibbonPanel / 返回生成的@ref SARibbonPanel 指针
 *
 * @see For other operations on SARibbonPanel, refer to @ref SARibbonCategory::takePanel
 *
 * 对SARibbonPanel的其他操作，参考 @ref SARibbonCategory::takePanel
 */
SARibbonPanel* SARibbonCategory::addPanel(const QString& title)
{
    return (d_ptr->addPanel(title));
}

/**
 * @brief Add panel/添加panel
 * @param panel panel ownership is managed by SARibbonCategory
 * panel的所有权SARibbonCategory来管理
 */
void SARibbonCategory::addPanel(SARibbonPanel* panel)
{
    d_ptr->addPanel(panel);
}

/**
 * @brief Qt Designer specific/Qt Designer专用
 * @param panel
 */
void SARibbonCategory::addPanel(QWidget* panel)
{
    SARibbonPanel* p = qobject_cast< SARibbonPanel* >(panel);
    if (p) {
        addPanel(p);
    }
}

/**
 * @brief Create a new panel and insert it at the index position/新建一个panel，并插入到index位置
 * @param title panel title/panel的title
 * @param index Insertion position, if index exceeds the number of panels in the category, it will be inserted at the
 * end/插入的位置，如果index超出category里panel的个数，将插入到最后
 * @return Returns a pointer to the generated @ref SARibbonPanel
 */
SARibbonPanel* SARibbonCategory::insertPanel(const QString& title, int index)
{
    return (d_ptr->insertPanel(title, index));
}

/**
 * @brief Find panel by name/通过名字查找panel
 * @param title
 * @return If there are duplicate names, only the first one that meets the condition will be returned/如果有重名，只会返回第一个符合条件的
 */
SARibbonPanel* SARibbonCategory::panelByName(const QString& title) const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->panelByName(title);
    }
    return nullptr;
}

/**
 * @brief Find panel by ObjectName/通过ObjectName查找panel
 * @param objname
 * @return
 */
SARibbonPanel* SARibbonCategory::panelByObjectName(const QString& objname) const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->panelByObjectName(objname);
    }
    return nullptr;
}

/**
 * @brief Find panel by index, returns nullptr if the index is out of range/通过索引找到panel，如果超过索引范围，会返回nullptr
 * @param index
 * @return Returns nullptr if the index is out of range/如果超过索引范围，会返回nullptr
 */
SARibbonPanel* SARibbonCategory::panelByIndex(int index) const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->panelByIndex(index);
    }
    return nullptr;
}

/**
 * @brief Find the index corresponding to panel/查找panel对应的索引
 * @param p
 * @return Returns -1 if not found/如果找不到，返回-1
 */
int SARibbonCategory::panelIndex(SARibbonPanel* p) const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->panelIndex(p);
    }
    return -1;
}

/**
 * @brief Move a Panel from from index to to index/移动一个Panel从from index到to index
 * @param from Index of the panel to move/要移动panel的index
 * @param to Position to move to/要移动到的位置
 */
void SARibbonCategory::movePanel(int from, int to)
{
    if (from == to) {
        return;
    }
    if (to < 0) {
        to = 0;
    }
    if (to >= panelCount()) {
        to = panelCount() - 1;
    }
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        lay->movePanel(from, to);
    }
}

/**
 * @brief Detach panel from SARibbonCategory management/把panel脱离SARibbonCategory的管理
 * @param panel Panel to extract/需要提取的panel
 * @return Returns true on success, otherwise false/成功返回true，否则返回false
 */
bool SARibbonCategory::takePanel(SARibbonPanel* panel)
{
    return (d_ptr->takePanel(panel));
}

/**
 * @brief Remove Panel, Category will directly recycle SARibbonPanel memory/移除Panel，Category会直接回收SARibbonPanel内存
 * @param panel Panel to remove/需要移除的panel
 * @note After removal, panel becomes a wild pointer. It is generally recommended to set the panel pointer to nullptr after the operation
 *
 * This operation is equivalent to:
 *
 * 此操作等同于：
 *
 * @code
 * SARibbonPanel* panel;
 * ...
 * category->takePanel(panel);
 * panel->hide();
 * panel->deleteLater();
 * @endcode
 * 移除后panel为野指针，一般操作完建议把panel指针设置为nullptr
 */
bool SARibbonCategory::removePanel(SARibbonPanel* panel)
{
    return (d_ptr->removePanel(panel));
}

/**
 * @brief Remove panel/移除panel
 * @param index Index of panel, returns false if it exceeds the limit/panel的索引，如果超出会返回false
 * @return Returns true on success/成功返回true
 */
bool SARibbonCategory::removePanel(int index)
{
    SARibbonPanel* p = panelByIndex(index);

    if (nullptr == p) {
        return (false);
    }
    return (removePanel(p));
}

/**
 * @brief Return all panels under Category/返回Category下的所有panel
 * @return List of all panels in the category/分类中所有panel的列表
 */
QList< SARibbonPanel* > SARibbonCategory::panelList() const
{
    return (d_ptr->panelList());
}

QSize SARibbonCategory::sizeHint() const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->sizeHint();
    }
    return QSize(1000, 100);
}

/**
 * @brief Returns true if it is a ContextCategory/如果是ContextCategory，此函数返回true
 * @return True if this is a context category, false otherwise/如果是上下文分类返回true，否则返回false
 */
bool SARibbonCategory::isContextCategory() const
{
    return (d_ptr->isContextCategory);
}

/**
 * @brief Return the number of panels/返回panel的个数
 * @return Number of panels in the category/分类中panel的数量
 */
int SARibbonCategory::panelCount() const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->panelCount();
    }
    return -1;
}

/**
 * @brief Determine if customization is allowed/判断是否可以自定义
 * @return True if customization is allowed, false otherwise/如果允许自定义返回true，否则返回false
 */
bool SARibbonCategory::isCanCustomize() const
{
    return (d_ptr->isCanCustomize);
}

/**
 * @brief Set whether customization is allowed/设置是否可以自定义
 * @param b True to allow customization, false to disallow/为true允许自定义，为false禁止自定义
 */
void SARibbonCategory::setCanCustomize(bool b)
{
    d_ptr->isCanCustomize = b;
}

/**
 * @brief Height of panel title bar/panel标题栏的高度
 * @return Current height of panel title bars/当前panel标题栏的高度
 */
int SARibbonCategory::panelTitleHeight() const
{
    return d_ptr->panelTitleHeight;
}

/**
 * @brief Set the height of panel/设置panel的高度
 * @param h New height for panel title bars/panel标题栏的新高度
 */
void SARibbonCategory::setPanelTitleHeight(int h)
{
    d_ptr->panelTitleHeight = h;
    iteratePanel([ h ](SARibbonPanel* p) -> bool {
        p->setTitleHeight(h);
        return true;
    });
}

/**
 * @brief Whether the panel title bar is displayed/是否panel显示标题栏
 * @return True if panel title bars are displayed, false otherwise/如果显示panel标题栏返回true，否则返回false
 */
bool SARibbonCategory::isEnableShowPanelTitle() const
{
    return d_ptr->enableShowPanelTitle;
}

/**
 * @brief Set to display panel title/设置显示panel标题
 * @param on True to display panel title bars, false to hide them/为true显示panel标题栏，为false隐藏它们
 */
void SARibbonCategory::setEnableShowPanelTitle(bool on)
{
    d_ptr->enableShowPanelTitle = on;
    iteratePanel([ on ](SARibbonPanel* p) -> bool {
        p->setEnableShowTitle(on);
        return true;
    });
}

/**
 * @brief Get the corresponding ribbonbar, returns null if not managed by ribbonbar/获取对应的ribbonbar，如果没有加入ribbonbar的管理，此值为null
 * @return Returns null if not managed by ribbonbar/如果没有加入ribbonbar的管理，此值为null
 */
SARibbonBar* SARibbonCategory::ribbonBar() const
{
    // 第一个par是stackwidget
    if (QWidget* par = parentWidget()) {
        // 理论此时是ribbonbar
        par = par->parentWidget();
        while (par) {
            if (SARibbonBar* ribbon = qobject_cast< SARibbonBar* >(par)) {
                return ribbon;
            }
            par = par->parentWidget();
        }
    }
    return nullptr;
}

/**
 * @brief Refresh the category layout, suitable for calling after changing the ribbon mode/刷新category的布局，适用于改变ribbon的模式之后调用
 */
void SARibbonCategory::updateItemGeometry()
{
#if SARIBBONCATEGORY_DEBUG_PRINT
    qDebug() << "SARibbonCategory name=" << categoryName() << " updateItemGeometry";
#endif
    d_ptr->updateItemGeometry();
}

/**
 * @brief Set whether to use animation when scrolling/设置滚动时是否使用动画
 * @param useAnimating True to use animation, false to scroll directly/为true使用动画，为false直接滚动
 */
void SARibbonCategory::setUseAnimatingScroll(bool useAnimating)
{
    d_ptr->isUseAnimating = useAnimating;
}

/**
 * @brief Whether to use animation when scrolling/滚动时是否使用动画
 * @return True if animation is used, false otherwise/如果使用动画返回true，否则返回false
 */
bool SARibbonCategory::isUseAnimatingScroll() const
{
    return d_ptr->isUseAnimating;
}

/**
 * @brief Set wheel scroll step (px)/设置滚轮滚动步长（px）
 * @param step Scroll step in pixels/以像素为单位的滚动步长
 */
void SARibbonCategory::setWheelScrollStep(int step)
{
    d_ptr->wheelScrollStep = qMax(10, step);  // 最小10像素
}

/**
 * @brief Wheel scroll step/滚轮的滚动步长
 * @return Current scroll step in pixels/当前以像素为单位的滚动步长
 */
int SARibbonCategory::wheelScrollStep() const
{
    return d_ptr->wheelScrollStep;
}

/**
 * @brief Set animation duration/设置动画持续时间
 * @param duration Animation duration in milliseconds/以毫秒为单位的动画持续时间
 */
void SARibbonCategory::setAnimationDuration(int duration)
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        lay->setAnimationDuration(duration);
    }
}

/**
 * @brief Animation duration/动画持续时间
 * @return Current animation duration in milliseconds, or -1 if not available/当前以毫秒为单位的动画持续时间，如果不可用则返回-1
 */
int SARibbonCategory::animationDuration() const
{
    if (SARibbonCategoryLayout* lay = categoryLayout()) {
        return lay->animationDuration();
    }
    return -1;
}

/**
 * @brief Determine whether the text of panel is allowed to wrap/判断panel的文字是否允许换行
 * @return True if text wrapping is enabled, false otherwise/如果启用文字换行返回true，否则返回false
 */
bool SARibbonCategory::isEnableWordWrap() const
{
    return d_ptr->enableWordWrap;
}

/**
 * @brief Set whether the text of panel buttons is allowed to wrap/设置panel的按钮文字允许换行
 * @param on True to enable text wrapping, false to disable/为true启用文字换行，为false禁用
 */
void SARibbonCategory::setEnableWordWrap(bool on)
{
    d_ptr->enableWordWrap = on;
    iteratePanel([ on ](SARibbonPanel* panel) -> bool {
        if (panel) {
            panel->setEnableWordWrap(on);
        }
        return true;
    });
    updateGeometry();
}

/**
 * @brief Set button maximum aspect ratio, this coefficient determines the maximum width of the button/设置按钮最大宽高比，这个系数决定按钮的最大宽度
 *
 * The maximum width of the button is button height * this coefficient. For example, if the button height is h,
 * then the maximum button width maxw = h * buttonMaximumAspectRatio
 * If the text cannot be fully displayed at this width, the button will not continue to expand horizontally,
 * and ... will be used instead of the incompletely displayed text
 *
 * 按钮的最大宽度为按钮高度*此系数，例如按钮高度为h，那么按钮最大宽度maxw=h*buttonMaximumAspectRatio
 * 如果在此宽度下文字还无法完全显示，那么按钮将不会继续横向扩展，将使用...替代未完全显示的文字
 *
 * @see buttonMaximumAspectRatio
 *
 * @note Users should not call @ref SARibbonCategory::setButtonMaximumAspectRatio to set,
 * but call @ref SARibbonBar::setButtonMaximumAspectRatio to set the aspect ratio
 *
 * 用户不应该调用@ref SARibbonCategory::setButtonMaximumAspectRatio 来设置，
 * 而是调用@ref SARibbonBar::setButtonMaximumAspectRatio 设置宽高比
 *
 * @param fac New maximum aspect ratio/新的最大宽高比
 */
void SARibbonCategory::setButtonMaximumAspectRatio(qreal fac)
{
    d_ptr->buttonMaximumAspectRatio = fac;
    iteratePanel([ fac ](SARibbonPanel* panel) -> bool {
        if (panel) {
            panel->setButtonMaximumAspectRatio(fac);
        }
        return true;
    });
}

/**
 * @brief Button maximum aspect ratio, this coefficient determines the maximum width of the button/按钮最大宽高比，这个系数决定按钮的最大宽度
 * @return Current button maximum aspect ratio/当前按钮最大宽高比
 * @see setButtonMaximumAspectRatio
 */
qreal SARibbonCategory::buttonMaximumAspectRatio() const
{
    return d_ptr->buttonMaximumAspectRatio;
}

/**
 * @brief This function will iterate through all panels under Category and execute the function pointer/此函数会遍历Category下的所有panel,执行函数指针
 * @param fp Function pointer returns false to stop iteration/函数指针返回false则停止迭代
 * @return Returns false indicating that all categories have not been iterated. The iteration was interrupted by receiving
 * a false return from the callback function/返回false代表没有迭代完所有的category，中途接收到回调函数的false返回而中断迭代
 */
bool SARibbonCategory::iteratePanel(FpPanelIterate fp) const
{
    const QList< SARibbonPanel* > ps = panelList();
    for (SARibbonPanel* p : ps) {
        if (!fp(p)) {
            return false;
        }
    }
    return true;
}

/**
 * @brief Set the alignment of Category/设置Category的对齐方式
 * @param al Alignment for the category/分类的对齐方式
 */
void SARibbonCategory::setCategoryAlignment(SARibbonAlignment al)
{
    SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout());
    if (lay) {
        lay->setCategoryAlignment(al);
    }
}

/**
 * @brief Category alignment/Category的对齐方式
 * @return Current alignment of the category/分类的当前对齐方式
 */
SARibbonAlignment SARibbonCategory::categoryAlignment() const
{
    SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout());
    if (lay) {
        return lay->categoryAlignment();
    }
    return SARibbonAlignment::AlignLeft;
}

/**
 * @brief Set the spacing of panel/设置panel的spacing
 * @param n Spacing between panels/panel之间的间距
 */
void SARibbonCategory::setPanelSpacing(int n)
{
    d_ptr->panelSpacing = n;
    iteratePanel([ n ](SARibbonPanel* panel) -> bool {
        if (panel) {
            panel->setSpacing(n);
        }
        return true;
    });
}

/**
 * @brief panel spacing/panel的spacing
 * @return Current spacing between panels/当前panel之间的间距
 */
int SARibbonCategory::panelSpacing() const
{
    return d_ptr->panelSpacing;
}

/**
 * @brief 设置panel的大按钮图标尺寸
 * @param largeSize
 */
void SARibbonCategory::setPanelLargeIconSize(const QSize& largeSize)
{
    d_ptr->panelLargeToolButtonIconSize = largeSize;
    iteratePanel([ largeSize ](SARibbonPanel* panel) -> bool {
        if (panel) {
            panel->setLargeIconSize(largeSize);
        }
        return true;
    });
}

/**
 * @brief panel的大按钮图标尺寸
 * @return
 */
QSize SARibbonCategory::panelLargeIconSize() const
{
    return d_ptr->panelLargeToolButtonIconSize;
}

/**
 * @brief 设置panel的小按钮图标尺寸
 * @param smallSize
 */
void SARibbonCategory::setPanelSmallIconSize(const QSize& smallSize)
{
    d_ptr->panelSmallToolButtonIconSize = smallSize;
    iteratePanel([ smallSize ](SARibbonPanel* panel) -> bool {
        if (panel) {
            panel->setSmallIconSize(smallSize);
        }
        return true;
    });
}

/**
 * @brief panel的小按钮图标尺寸
 * @return
 */
QSize SARibbonCategory::panelSmallIconSize() const
{
    return d_ptr->panelSmallToolButtonIconSize;
}

/**
 * @brief Set the icon size of panel buttons, large action is not affected/设置panel按钮的icon尺寸，large action不受此尺寸影响
 * @param smallSize New icon size for panel buttons/panel按钮的新图标大小
 */
void SARibbonCategory::setPanelToolButtonIconSize(const QSize& smallSize, const QSize& largeSize)
{
    d_ptr->panelSmallToolButtonIconSize = smallSize;
    d_ptr->panelLargeToolButtonIconSize = largeSize;
    iteratePanel([ smallSize, largeSize ](SARibbonPanel* panel) -> bool {
        if (panel) {
            panel->setToolButtonIconSize(smallSize, largeSize);
        }
        return true;
    });
}

/**
 * @brief Icon size of panel buttons, large action is not affected/panel按钮的icon尺寸，large action不受此尺寸影响
 * @return Current icon size for panel buttons/当前panel按钮的图标大小
 */
QPair< QSize, QSize > SARibbonCategory::panelToolButtonIconSize() const
{
    return qMakePair(d_ptr->panelSmallToolButtonIconSize, d_ptr->panelLargeToolButtonIconSize);
}

/**
 * @brief 滚动事件
 *
 * 在内容超出category的宽度情况下，滚轮可滚动panel
 * @param event
 */
void SARibbonCategory::wheelEvent(QWheelEvent* event)
{
    d_ptr->doWheelEvent(event);
}

void SARibbonCategory::changeEvent(QEvent* event)
{
    switch (event->type()) {
    case QEvent::StyleChange: {
        if (layout()) {
#if SARIBBONCATEGORY_DEBUG_PRINT
            qDebug() << "SARibbonCategory changeEvent(StyleChange),categoryName=" << categoryName();
#endif
            layout()->invalidate();
        }
    } break;
    case QEvent::FontChange: {
#if SARIBBONCATEGORY_DEBUG_PRINT
        qDebug() << "SARibbonCategory changeEvent(FontChange),categoryName=" << categoryName();
#endif
        QFont f = font();
        iteratePanel([ f ](SARibbonPanel* p) -> bool {
            p->setFont(f);
            return true;
        });
        if (layout()) {
            layout()->invalidate();
        }
    } break;
    default:
        break;
    };
    return QWidget::changeEvent(event);
}

/**
 * @brief Mark this as a context label/标记这个是上下文标签
 * @param isContextCategory True to mark as context category, false otherwise/为true标记为上下文分类，否则为false
 */
void SARibbonCategory::markIsContextCategory(bool isContextCategory)
{
    d_ptr->isContextCategory = isContextCategory;
}

/**
 * @brief Get SARibbonCategoryLayoutlayout/获取SARibbonCategoryLayoutlayout
 * @return Pointer to the category layout, or nullptr if not available/指向分类布局的指针，如果不可用则返回nullptr
 */
SARibbonCategoryLayout* SARibbonCategory::categoryLayout() const
{
    return qobject_cast< SARibbonCategoryLayout* >(layout());
}

bool SARibbonCategory::event(QEvent* e)
{
#if SARIBBONCATEGORY_DEBUG_PRINT
    if (e->type() != QEvent::Paint) {
        qDebug() << "SARibbonCategory event(" << e->type() << "),name=" << categoryName();
    }
#endif
    return QWidget::event(e);
}

//===================================================
// SARibbonCategoryScrollButton
//===================================================

/**
 * @brief Constructor with arrow type and optional parent/带箭头类型和可选父级的构造函数
 * @param arr Arrow type for the button/按钮的箭头类型
 * @param p Parent widget/父级控件
 */
SARibbonCategoryScrollButton::SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p) : QToolButton(p)
{
    setArrowType(arr);
}

/**
 * @brief Destructor/析构函数
 */
SARibbonCategoryScrollButton::~SARibbonCategoryScrollButton()
{
}

/*** End of inlined file: SARibbonCategory.cpp ***/

/*** Start of inlined file: SARibbonCategoryLayout.cpp ***/
#include <QLayoutItem>

#include <QApplication>
#include <QPropertyAnimation>

#ifndef SARibbonCategoryLayout_DEBUG_PRINT
#define SARibbonCategoryLayout_DEBUG_PRINT 0
#endif
#if SARibbonCategoryLayout_DEBUG_PRINT
#include <QDebug>
#endif
/**
 * @brief The SARibbonCategoryLayoutPrivate class
 */
class SARibbonCategoryLayout::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonCategoryLayout)
public:
    PrivateData(SARibbonCategoryLayout* p);
    // 计算所有元素的sizehint总宽度
    int totalSizeHintWidth() const;

public:
    bool mDirty { true };
    bool mIsRightScrollBtnShow { false };                       ///< 标记右滚动按钮是否需要显示
    bool mIsLeftScrollBtnShow { false };                        ///< 标记左滚动按钮是否需要显示
    SARibbonCategoryScrollButton* mLeftScrollBtn { nullptr };   ///< 在区域无法显示时显示的按钮
    SARibbonCategoryScrollButton* mRightScrollBtn { nullptr };  ///< 在区域无法显示时显示的按钮
    int mTotalWidth { 0 };
    int mXBase { 0 };
    QSize mSizeHint;
    QSize mMinSizeHint;
    QList< SARibbonCategoryLayoutItem* > mItemList;
    SARibbonAlignment mCategoryAlignment { SARibbonAlignment::AlignLeft };  ///< 对齐方式
    // 动画相关
    QPropertyAnimation* mScrollAnimation { nullptr };
    int mTargetScrollPosition { 0 };
};

//=============================================================
// SARibbonCategoryLayoutPrivate
//=============================================================

SARibbonCategoryLayout::PrivateData::PrivateData(SARibbonCategoryLayout* p) : q_ptr(p)
{
}

/**
 * @brief 计算所有元素的SizeHint宽度总和
 * @return
 */
int SARibbonCategoryLayout::PrivateData::totalSizeHintWidth() const
{
    int total    = 0;
    QMargins mag = q_ptr->contentsMargins();
#if SARibbonCategoryLayout_DEBUG_PRINT
    int debug_i__ = 0;
    QString debug_totalSizeHintWidth__;
#endif
    if (!mag.isNull()) {
        total += (mag.left() + mag.right());
    }
    // 先计算总长
    for (SARibbonCategoryLayoutItem* item : qAsConst(mItemList)) {
        if (item->isEmpty()) {
// 如果是hide就直接跳过
#if SARibbonCategoryLayout_DEBUG_PRINT
            ++debug_i__;
            debug_totalSizeHintWidth__ +=
                QString("   [%1](%2)is empty skip\n").arg(debug_i__).arg(item->toPanelWidget()->panelName());
#endif
            continue;
        }
        // 这里要使用widget()->sizeHint()，因为panel的标题会影总体布局，此处需要修改
        //  TODO
        QSize panelSize = item->widget()->sizeHint();
        QSize SeparatorSize(0, 0);
        if (item->separatorWidget) {
            SeparatorSize = item->separatorWidget->sizeHint();
        }
        total += panelSize.width();
        total += SeparatorSize.width();
#if SARibbonCategoryLayout_DEBUG_PRINT
        ++debug_i__;
        debug_totalSizeHintWidth__ += QString("|-[%1]panelSize=(%2,%3),SeparatorSize=(%4,%5),name=(%6) \n")
                                          .arg(debug_i__)
                                          .arg(panelSize.width())
                                          .arg(panelSize.height())
                                          .arg(SeparatorSize.width())
                                          .arg(SeparatorSize.height())
                                          .arg(item->toPanelWidget()->panelName());
#endif
    }
#if SARibbonCategoryLayout_DEBUG_PRINT
    qDebug() << "SARibbonCategoryLayout.totalSizeHintWidth=" << total;
    qDebug().noquote() << debug_totalSizeHintWidth__;
#endif
    return (total);
}

//=============================================================
// SARibbonCategoryLayout
//=============================================================

SARibbonCategoryLayout::SARibbonCategoryLayout(SARibbonCategory* parent)
    : QLayout(parent), d_ptr(new SARibbonCategoryLayout::PrivateData(this))
{
    setContentsMargins(1, 1, 1, 1);
    d_ptr->mLeftScrollBtn  = new SARibbonCategoryScrollButton(Qt::LeftArrow, parent);
    d_ptr->mRightScrollBtn = new SARibbonCategoryScrollButton(Qt::RightArrow, parent);
    d_ptr->mLeftScrollBtn->setVisible(false);
    d_ptr->mRightScrollBtn->setVisible(false);
    connect(d_ptr->mLeftScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onLeftScrollButtonClicked);
    connect(d_ptr->mRightScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onRightScrollButtonClicked);
    setupAnimateScroll();
}

SARibbonCategoryLayout::~SARibbonCategoryLayout()
{
    while (auto item = takePanelItem(0)) {
        delete item;
    }
}

SARibbonCategory* SARibbonCategoryLayout::ribbonCategory() const
{
    return (qobject_cast< SARibbonCategory* >(parentWidget()));
}

void SARibbonCategoryLayout::addItem(QLayoutItem* item)
{
    Q_UNUSED(item);
    qWarning() << tr("in SARibbonCategoryLayout cannot addItem,use addPanel instead");
    invalidate();
}

/**
 * @brief 返回panel的layout
 * @param index 索引
 * @return
 * @note 注意，panel是和分割线一起的，但这个只返回一个panel对应的layout
 */
QLayoutItem* SARibbonCategoryLayout::itemAt(int index) const
{
    SARibbonCategoryLayoutItem* item = d_ptr->mItemList.value(index, nullptr);

    return (item);
}

/**
 * @brief 提取layout
 * @param index
 * @return
 */
QLayoutItem* SARibbonCategoryLayout::takeAt(int index)
{
    QLayoutItem* r = takePanelItem(index);
    invalidate();
    return r;
}

SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePanelItem(int index)
{
    if ((index >= 0) && (index < d_ptr->mItemList.size())) {
        SARibbonCategoryLayoutItem* item = d_ptr->mItemList.takeAt(index);
        if (item->widget()) {
            item->widget()->hide();
        }
        if (item->separatorWidget) {
            item->separatorWidget->hide();
        }
        return (item);
    }
    return (nullptr);
}

SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePanelItem(SARibbonPanel* panel)
{
    for (int i = 0; i < d_ptr->mItemList.size(); ++i) {
        SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ];
        if (item->widget() == panel) {
            return (takePanelItem(i));
        }
    }
    return (nullptr);
}

/**
 * @brief 移除panel，对应的分割线也会删除
 * @param panel
 * @return
 */
bool SARibbonCategoryLayout::takePanel(SARibbonPanel* panel)
{
    SARibbonCategoryLayoutItem* i = takePanelItem(panel);
    if (i) {
        SARibbonSeparatorWidget* sp = i->separatorWidget;
        if (sp) {
            sp->deleteLater();
        }
        delete i;
        invalidate();
        return true;
    }
    return false;
}

int SARibbonCategoryLayout::count() const
{
    return (d_ptr->mItemList.size());
}

void SARibbonCategoryLayout::addPanel(SARibbonPanel* panel)
{
    insertPanel(d_ptr->mItemList.count(), panel);
}

/**
 * @brief 插入一个panel
 * @param index 索引
 * @param panel
 * @return 返回对应的分割线SARibbonSeparatorWidget
 * @note 在SARibbonCategoryLayout的布局中，一个panel会携带一个分割线
 */
void SARibbonCategoryLayout::insertPanel(int index, SARibbonPanel* panel)
{
    index                            = qMax(0, index);
    index                            = qMin(d_ptr->mItemList.count(), index);
    SARibbonCategoryLayoutItem* item = new SARibbonCategoryLayoutItem(panel);

    // 分割线
    item->separatorWidget = RibbonSubElementFactory->createRibbonSeparatorWidget(parentWidget());
    // 插入list中
    d_ptr->mItemList.insert(index, item);
    // 标记需要重新计算尺寸
    invalidate();
}

QSize SARibbonCategoryLayout::sizeHint() const
{
    if (d_ptr->mSizeHint.isNull()) {
        SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this);
        that->updateGeometryArr();
    }
    return (d_ptr->mSizeHint);
}

QSize SARibbonCategoryLayout::minimumSize() const
{
    if (d_ptr->mMinSizeHint.isNull()) {
        SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this);
        that->updateGeometryArr();
    }
    return (d_ptr->mMinSizeHint);
}

/**
 * @brief SARibbonCategory充满整个stacked widget
 * @return
 */
Qt::Orientations SARibbonCategoryLayout::expandingDirections() const
{
    return (Qt::Horizontal | Qt::Vertical);
}

void SARibbonCategoryLayout::invalidate()
{
    d_ptr->mDirty = true;
    QLayout::invalidate();
}
/**
 * @brief category的内容尺寸（把margins减去）
 * @return
 */
QSize SARibbonCategoryLayout::categoryContentSize() const
{
    SARibbonCategory* category = ribbonCategory();
    QSize s                    = category->size();
    QMargins mag               = contentsMargins();
    if (!mag.isNull()) {
        s.rheight() -= (mag.top() + mag.bottom());
        s.rwidth() -= (mag.right() + mag.left());
    }
    return (s);
}

/**
 * @brief 更新尺寸
 */
void SARibbonCategoryLayout::updateGeometryArr()
{
    SARibbonCategory* category = ribbonCategory();
    if (nullptr == category) {
        return;
    }
    int categoryWidth = category->width();
    QMargins mag      = contentsMargins();
    int height        = category->height();
    int y             = 0;

    if (!mag.isNull()) {
        y = mag.top();
        height -= (mag.top() + mag.bottom());
        // categoryWidth不能把mag减去，减去后会导致categoryWidth不是实际的categoryWidth
        // categoryWidth -= (mag.right() + mag.left());
    }
    // total 是总宽，不是x坐标系，x才是坐标系
    int total = d_ptr->totalSizeHintWidth();

    // 扩展的宽度
    int expandWidth = 0;

    // 判断是否需要滚动，总长度超过宽度就需要滚动
    bool needsScrolling = (total > categoryWidth);

#if SARibbonCategoryLayout_DEBUG_PRINT
    qDebug() << "SARibbonCategoryLayout::updateGeometryArr" << "\n  |-category name=" << category->categoryName()  //
             << "\n  |-category height=" << height                                                                 //
             << "\n  |-totalSizeHintWidth=" << total                                                               //
             << "\n  |-y=" << y                                                                                    //
             << "\n  |-expandWidth:" << expandWidth                                                                //
             << "\n  |-mag=" << mag;
#endif

    if (needsScrolling) {
        // 超过总长度，需要显示滚动按钮
        if (0 == d_ptr->mXBase) {
            // 已经移动到最左，需要可以向右移动
            d_ptr->mIsRightScrollBtnShow = true;
            d_ptr->mIsLeftScrollBtnShow  = false;
        } else if (d_ptr->mXBase <= (categoryWidth - total)) {
            // 已经移动到最右，需要可以向左移动
            d_ptr->mIsRightScrollBtnShow = false;
            d_ptr->mIsLeftScrollBtnShow  = true;
        } else {
            // 移动到中间两边都可以动
            d_ptr->mIsRightScrollBtnShow = true;
            d_ptr->mIsLeftScrollBtnShow  = true;
        }
    } else {
        // 说明total 小于 categoryWidth
        // 记录可以扩展的数量
        int canExpandingCount        = 0;
        d_ptr->mIsRightScrollBtnShow = false;
        d_ptr->mIsLeftScrollBtnShow  = false;
        // 这个是避免一开始totalWidth > categorySize.width()，通过滚动按钮调整了m_d->mBaseX
        // 随之调整了窗体尺寸，调整后totalWidth < categorySize.width()导致category在原来位置
        // 无法显示，必须这里把mBaseX设置为0

        d_ptr->mXBase = 0;
        //

        for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) {
            if (SARibbonPanel* p = qobject_cast< SARibbonPanel* >(item->widget())) {
                if (p->isExpanding()) {
                    // panel可扩展
                    ++canExpandingCount;
                }
            }
        }
        // 计算可扩展的宽度
        if (canExpandingCount > 0) {
            expandWidth = (categoryWidth - total) / canExpandingCount;
        } else {
            expandWidth = 0;
        }
    }
    int x = d_ptr->mXBase;
    if ((categoryAlignment() == SARibbonAlignment::AlignCenter) && (total < categoryWidth) && (0 == expandWidth)) {
        // 如果是居中对齐，同时没有伸缩的panel，同时总宽度没有超过category的宽度
        x = (categoryWidth - total) / 2;
    }
    total = 0;  // total重新计算
    // 先按照sizeHint设置所有的尺寸
    for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) {
        if (item->isEmpty()) {
            // 如果是hide就直接跳过
            if (item->separatorWidget) {
                // panel hide分割线也要hide
                item->separatorWidget->hide();
            }
            item->mWillSetGeometry          = QRect(0, 0, 0, 0);
            item->mWillSetSeparatorGeometry = QRect(0, 0, 0, 0);
            continue;
        }
        SARibbonPanel* p = item->toPanelWidget();
        if (nullptr == p) {
            qDebug() << "unknow widget in SARibbonCategoryLayout";
            continue;
        }
        // p->layout()->update();
        QSize panelSize = p->sizeHint();
        QSize SeparatorSize(0, 0);
        if (item->separatorWidget) {
            SeparatorSize = item->separatorWidget->sizeHint();
        }
        if (p->isExpanding()) {
            // 可扩展，就把panel扩展到最大
            panelSize.setWidth(panelSize.width() + expandWidth);
        }
        int w = panelSize.width();

        item->mWillSetGeometry = QRect(x, y, w, height);
        x += w;
        total += w;
        w                               = SeparatorSize.width();
        item->mWillSetSeparatorGeometry = QRect(x, y, w, height);
        x += w;
        total += w;
    }
    d_ptr->mTotalWidth  = total;
    d_ptr->mSizeHint    = QSize(d_ptr->mTotalWidth, height);
    d_ptr->mMinSizeHint = QSize(categoryWidth, height);
#if SARibbonCategoryLayout_DEBUG_PRINT
    qDebug() << "  SARibbonCategoryLayout updateGeometryArr,SizeHint=" << d_ptr->mSizeHint
             << ",Category name=" << category->categoryName();
#endif
}

/**
 * @brief 执行layout调整
 */
void SARibbonCategoryLayout::doLayout()
{
    if (d_ptr->mDirty) {
        updateGeometryArr();
    }
    if (d_ptr->mItemList.isEmpty()) {
        if (d_ptr->mLeftScrollBtn->isVisible()) {
            d_ptr->mLeftScrollBtn->hide();
        }
        if (d_ptr->mRightScrollBtn->isVisible()) {
            d_ptr->mRightScrollBtn->hide();
        }
        return;
    }
    SARibbonCategory* category = ribbonCategory();
    // 两个滚动按钮的位置永远不变
    d_ptr->mLeftScrollBtn->setGeometry(0, 0, 12, category->height());
    d_ptr->mRightScrollBtn->setGeometry(category->width() - 12, 0, 12, category->height());
    QList< QWidget* > showWidgets, hideWidgets;
#if SARibbonCategoryLayout_DEBUG_PRINT
    int debug_i__(0);
    qDebug() << "SARibbonCategoryLayout::doLayout(),name=" << category->categoryName();
#endif
    const int itemsize = d_ptr->mItemList.size();
    for (int i = 0; i < itemsize; ++i) {
        SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ];
        if (item->isEmpty()) {
            hideWidgets << item->widget();
            if (item->separatorWidget) {
                hideWidgets << item->separatorWidget;
            }
#if SARibbonCategoryLayout_DEBUG_PRINT
            qDebug() << "  |-[" << debug_i__ << "]panelName(" << item->toPanelWidget()->panelName() << ",will hide";
            ++debug_i__;
#endif
        } else {
            //! 这里不能用item->setGeometry(item->mWillSetGeometry);这样会得到一个很奇怪的显示效果
            //! 就是窗口的最左边不会移出去，而是把最右边压缩，
            item->widget()->setGeometry(item->mWillSetGeometry);
            //            item->widget()->setFixedSize(item->mWillSetGeometry.size());
            //            item->widget()->move(item->mWillSetGeometry.topLeft());
            //            item->setGeometry(item->mWillSetGeometry);
            showWidgets << item->widget();
            if (item->separatorWidget) {
                item->separatorWidget->setGeometry(item->mWillSetSeparatorGeometry);
                if (i == itemsize - 1) {
                    // 最后一个panel的分割线隐藏
                    hideWidgets << item->separatorWidget;
                } else {
                    showWidgets << item->separatorWidget;
                }
            }
#if SARibbonCategoryLayout_DEBUG_PRINT
            qDebug() << "  |-[" << debug_i__ << "]panelName(" << item->toPanelWidget()->panelName()
                     << "),willSetGeometry:" << item->mWillSetGeometry
                     << ",WillSetSeparatorGeometry:" << item->mWillSetSeparatorGeometry;
            ++debug_i__;
#endif
        }
    }

    d_ptr->mRightScrollBtn->setVisible(d_ptr->mIsRightScrollBtnShow);
    d_ptr->mLeftScrollBtn->setVisible(d_ptr->mIsLeftScrollBtnShow);
    if (d_ptr->mIsRightScrollBtnShow) {
        d_ptr->mRightScrollBtn->raise();
    }
    if (d_ptr->mIsLeftScrollBtnShow) {
        d_ptr->mLeftScrollBtn->raise();
    }
    // 不在上面那里进行show和hide因为这会触发SARibbonPanelLayout的重绘，导致循环绘制，非常影响效率
    for (QWidget* w : qAsConst(showWidgets)) {
        if (!w->isVisible()) {
            w->show();
        }
    }
    for (QWidget* w : qAsConst(hideWidgets)) {
        if (w->isVisible()) {
            w->hide();
        }
    }
    // 最后一个分割线隐藏
}

/**
 * @brief 返回所有panels
 * @return
 */
QList< SARibbonPanel* > SARibbonCategoryLayout::panels() const
{
    QList< SARibbonPanel* > res;

    for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) {
        SARibbonPanel* p = item->toPanelWidget();
        res.append(p);
    }
    return (res);
}

/**
 * @brief 通过ObjectName查找panel
 * @param objname
 * @return
 */
SARibbonPanel* SARibbonCategoryLayout::panelByObjectName(const QString& objname) const
{
    for (SARibbonCategoryLayoutItem* item : d_ptr->mItemList) {
        if (SARibbonPanel* panel = item->toPanelWidget()) {
            if (panel->objectName() == objname) {
                return panel;
            }
        }
    }
    return nullptr;
}

/**
 * @brief 通过名字查找panel
 * @param title
 * @return 如果有重名，只会返回第一个符合条件的
 */
SARibbonPanel* SARibbonCategoryLayout::panelByName(const QString& panelname) const
{
    for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) {
        if (SARibbonPanel* panel = item->toPanelWidget()) {
            if (panel->panelName() == panelname) {
                return (panel);
            }
        }
    }
    return (nullptr);
}

/**
 * @brief 通过索引找到panel，如果超过索引范围，会返回nullptr
 * @param i
 * @return
 */
SARibbonPanel* SARibbonCategoryLayout::panelByIndex(int i) const
{
    if (i >= 0 && i < d_ptr->mItemList.size()) {
        return d_ptr->mItemList[ i ]->toPanelWidget();
    }
    return nullptr;
}

/**
 * @brief 移动panel
 * @param from
 * @param to
 */
void SARibbonCategoryLayout::movePanel(int from, int to)
{
    d_ptr->mItemList.move(from, to);
    doLayout();
}

/**
 * @brief 返回panel的个数
 * @return
 */
int SARibbonCategoryLayout::panelCount() const
{
    return d_ptr->mItemList.size();
}

/**
 * @brief 查找panel对应的索引
 * @param p
 * @return 如果找不到，返回-1
 */
int SARibbonCategoryLayout::panelIndex(SARibbonPanel* p) const
{
    int c = panelCount();

    for (int i = 0; i < c; ++i) {
        if (d_ptr->mItemList[ i ]->toPanelWidget() == p) {
            return (i);
        }
    }
    return (-1);
}

/**
 * @brief 获取所有的panel
 * @return
 */
QList< SARibbonPanel* > SARibbonCategoryLayout::panelList() const
{
    QList< SARibbonPanel* > res;

    for (SARibbonCategoryLayoutItem* i : qAsConst(d_ptr->mItemList)) {
        if (SARibbonPanel* p = i->toPanelWidget()) {
            res.append(p);
        }
    }
    return (res);
}

/**
 * @brief 执行滚动
 * @param px
 */
void SARibbonCategoryLayout::scroll(int px)
{
    // 计算新位置
    int targetX = d_ptr->mXBase + px;
    // 直接设置位置
    scrollTo(targetX);
}

/**
 * @brief 滚动到指定位置
 * @param targetX
 */
void SARibbonCategoryLayout::scrollTo(int targetX)
{
    setScrollPosition(targetX);
}

/**
 * @brief 带动画的滚动
 * @param px
 */
void SARibbonCategoryLayout::scrollByAnimate(int px)
{
    int targetX = d_ptr->mXBase + px;
    scrollToByAnimate(targetX);
}

/**
 * @brief 滚动到指定位置，带动画
 * @param px
 */
void SARibbonCategoryLayout::scrollToByAnimate(int targetX)
{
    QPropertyAnimation* animation = d_ptr->mScrollAnimation;
    if (!animation) {
        scrollTo(targetX);
    }
    if (isAnimatingScroll() && targetX == d_ptr->mTargetScrollPosition) {
        return;  // 已经是目标位置
    }
    // 计算边界
    const int availableWidth     = categoryContentSize().width();
    const int minBase            = qMin(availableWidth - d_ptr->mTotalWidth, 0);
    d_ptr->mTargetScrollPosition = qBound(minBase, targetX, 0);

    // 如果动画正在进行，停止当前动画
    if (animation->state() == QPropertyAnimation::Running) {
        animation->stop();
    }

    // 设置动画参数
    animation->setStartValue(d_ptr->mXBase);
    animation->setEndValue(d_ptr->mTargetScrollPosition);
    animation->start();
}

/**
 * @brief 滚动后的位置
 * @return
 */
int SARibbonCategoryLayout::scrollPosition() const
{
    return d_ptr->mXBase;
}

/**
 * @brief 设置滚动位置
 * @param px
 */
void SARibbonCategoryLayout::setScrollPosition(int pos)
{
    // 边界检查
    const int availableWidth = categoryContentSize().width();
    const int minBase        = qMin(availableWidth - d_ptr->mTotalWidth, 0);
    const int newXBase       = qBound(minBase, pos, 0);

    if (d_ptr->mXBase != newXBase) {
        d_ptr->mXBase = newXBase;
        invalidate();  // 标记需要重新布局

        // 立即执行布局更新（而不是等待事件循环）
        if (parentWidget()) {
            // setGeometry(parentWidget()->geometry());
            parentWidget()->update();
        }
    }
}

/**
 * @brief 判断是否在滚动动画中
 * @return
 */
bool SARibbonCategoryLayout::isAnimatingScroll() const
{
    return d_ptr->mScrollAnimation->state() == QPropertyAnimation::Running;
}

/**
 * @brief 判断是否滚动过
 * @return
 */
bool SARibbonCategoryLayout::isScrolled() const
{
    return (d_ptr->mXBase != 0);
}

/**
 * @brief 这个宽度是实际内容的宽度，有可能大于size.width，也有可能小于
 * @return
 */
int SARibbonCategoryLayout::categoryTotalWidth() const
{
    return d_ptr->mTotalWidth;
}

/**
   @brief 设置Category的对齐方式

   居中对齐会让panel以居中进行对齐
   @param al
 */
void SARibbonCategoryLayout::setCategoryAlignment(SARibbonAlignment al)
{
    if (d_ptr->mCategoryAlignment != al) {
        d_ptr->mCategoryAlignment = al;
    }
}

/**
   @brief Category的对齐方式
   @return
 */
SARibbonAlignment SARibbonCategoryLayout::categoryAlignment() const
{
    return d_ptr->mCategoryAlignment;
}

/**
 * @brief 设置动画的持续时间
 * @param duration 毫秒
 */
void SARibbonCategoryLayout::setAnimationDuration(int duration)
{
    if (d_ptr->mScrollAnimation) {
        d_ptr->mScrollAnimation->setDuration(qMax(50, duration));  // 最小50ms
    }
}

/**
 * @brief 动画的持续时间，默认300ms
 * @return 如果没有设置动画，返回-1
 */
int SARibbonCategoryLayout::animationDuration() const
{
    if (d_ptr->mScrollAnimation) {
        return d_ptr->mScrollAnimation->duration();
    }
    return -1;
}

void SARibbonCategoryLayout::setupAnimateScroll()
{
    if (!d_ptr->mScrollAnimation) {
        // 初始化滚动动画
        d_ptr->mScrollAnimation = new QPropertyAnimation(this, "scrollPosition", this);
        d_ptr->mScrollAnimation->setDuration(300);                       // 动画时长300ms
        d_ptr->mScrollAnimation->setEasingCurve(QEasingCurve::OutQuad);  // 缓动曲线
        d_ptr->mTargetScrollPosition = d_ptr->mXBase;
    }
}

void SARibbonCategoryLayout::onLeftScrollButtonClicked()
{
    SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget());
    int width                  = category->width();
    width /= 2;
    scrollByAnimate(width);
}

void SARibbonCategoryLayout::onRightScrollButtonClicked()
{
    SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget());
    int width                  = category->width();
    width /= 2;
    scrollByAnimate(-width);
}

void SARibbonCategoryLayout::setGeometry(const QRect& rect)
{
    QRect old = geometry();
    if (old == rect) {
        return;
    }
#if SARibbonCategoryLayout_DEBUG_PRINT
    qDebug() << "===========SARibbonCategoryLayout.setGeometry(" << rect << "(" << ribbonCategory()->categoryName()
             << ")=======";
#endif
    QLayout::setGeometry(rect);
    d_ptr->mDirty = false;
    updateGeometryArr();
    doLayout();
}
//=============================================================
// SARibbonCategoryLayoutItem
//=============================================================

SARibbonCategoryLayoutItem::SARibbonCategoryLayoutItem(SARibbonPanel* w) : QWidgetItem(w)
{
    separatorWidget = nullptr;
}

SARibbonCategoryLayoutItem::~SARibbonCategoryLayoutItem()
{
}

SARibbonPanel* SARibbonCategoryLayoutItem::toPanelWidget()
{
    return qobject_cast< SARibbonPanel* >(widget());
}

/*** End of inlined file: SARibbonCategoryLayout.cpp ***/

/*** Start of inlined file: SARibbonContextCategory.cpp ***/
#include <QList>
#include <QVariant>

#include <QDebug>

/**
 * @brief The SARibbonCategoryData class
 */
class SAPrivateRibbonCategoryData
{
public:
    SARibbonCategory* categoryPage;
};

/**
 * @brief The SARibbonContextCategoryPrivate class
 */
class SARibbonContextCategory::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonContextCategory)
public:
    PrivateData(SARibbonContextCategory* p);

public:
    QList< SAPrivateRibbonCategoryData > mCategoryDataList;
    QVariant mContextID;
    QColor mContextColor;
    QString mContextTitle;
};
SARibbonContextCategory::PrivateData::PrivateData(SARibbonContextCategory* p) : q_ptr(p)
{
}

//===================================================
// SARibbonContextCategory
//===================================================
SARibbonContextCategory::SARibbonContextCategory(QWidget* parent)
    : QObject(parent), d_ptr(new SARibbonContextCategory::PrivateData(this))
{
}

SARibbonContextCategory::~SARibbonContextCategory()
{
}

/**
 * @brief 添加标签
 * @param title 标签名字
 */
SARibbonCategory* SARibbonContextCategory::addCategoryPage(const QString& title)
{
    SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(parentWidget());
    category->setCategoryName(title);
    addCategoryPage(category);
    return (category);
}

/**
 * @brief 添加标签
 * @param page
 */
void SARibbonContextCategory::addCategoryPage(SARibbonCategory* category)
{
    if (isHaveCategory(category)) {
        // cn:SARibbonContextCategory已经持有标签：%1，将跳过
        qWarning() << tr("SARibbonContextCategory have category %1,will skip").arg(category->categoryName());
        return;
    }
    category->markIsContextCategory(true);
    connect(category, &SARibbonCategory::categoryNameChanged, this, &SARibbonContextCategory::onCategoryTitleChanged);
    SAPrivateRibbonCategoryData catData;
    catData.categoryPage = category;
    d_ptr->mCategoryDataList.append(catData);
    category->installEventFilter(this);
    Q_EMIT categoryPageAdded(category);
}

int SARibbonContextCategory::categoryCount() const
{
    return (d_ptr->mCategoryDataList.count());
}

void SARibbonContextCategory::setId(const QVariant& id)
{
    d_ptr->mContextID = id;
}

QVariant SARibbonContextCategory::id() const
{
    return (d_ptr->mContextID);
}

void SARibbonContextCategory::setContextColor(const QColor color)
{
    d_ptr->mContextColor = color;
}

QColor SARibbonContextCategory::contextColor() const
{
    return (d_ptr->mContextColor);
}

QWidget* SARibbonContextCategory::parentWidget() const
{
    return (qobject_cast< QWidget* >(parent()));
}

bool SARibbonContextCategory::eventFilter(QObject* watched, QEvent* e)
{
    if (nullptr == watched) {
        return (false);
    }
    switch (e->type()) {
    case QEvent::Close: {
        SARibbonCategory* c = qobject_cast< SARibbonCategory* >(watched);
        if (c) {
#ifdef SA_RIBBON_DEBUG_HELP_DRAW
            qDebug() << " -----------> close event";
#endif
            takeCategory(c);
        }
    } break;

    default:
        break;
    }
    return (false);
}

/**
 * @brief 获取上下文标签的标题
 * @return
 */
QString SARibbonContextCategory::contextTitle() const
{
    return (d_ptr->mContextTitle);
}

/**
 * @brief 设置上下文标签的标题，标题仅在office模式下显示 @ref SARibbonBar::RibbonStyle
 * @param contextTitle
 * @note 此函数会触发信号@sa contextTitleChanged
 */
void SARibbonContextCategory::setContextTitle(const QString& contextTitle)
{
    d_ptr->mContextTitle = contextTitle;
    Q_EMIT contextTitleChanged(contextTitle);
}

/**
 * @brief 获取对应的tab页
 * @param index
 * @return
 */
SARibbonCategory* SARibbonContextCategory::categoryPage(int index)
{
    return (d_ptr->mCategoryDataList[ index ].categoryPage);
}

/**
 * @brief 获取所有的SARibbonCategory*
 * @return
 */
QList< SARibbonCategory* > SARibbonContextCategory::categoryList() const
{
    QList< SARibbonCategory* > res;

    for (const SAPrivateRibbonCategoryData& c : qAsConst(d_ptr->mCategoryDataList)) {
        res.append(c.categoryPage);
    }
    return (res);
}

/**
 * @brief 移除这个category，这时SARibbonContextCategory不再管理这个category
 * @param category
 * @return 成功移除返回true
 */
bool SARibbonContextCategory::takeCategory(SARibbonCategory* category)
{
    for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) {
        if (d_ptr->mCategoryDataList[ i ].categoryPage == category) {
            d_ptr->mCategoryDataList.takeAt(i);
            return (true);
        }
    }
    return (false);
}

/**
 * @brief 判断上下文是否维护了此SARibbonCategory
 * @param category
 * @return
 */
bool SARibbonContextCategory::isHaveCategory(SARibbonCategory* category) const
{
    for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) {
        if (d_ptr->mCategoryDataList[ i ].categoryPage == category) {
            return (true);
        }
    }
    return (false);
}

/**
 * @brief hide contextCategory/隐藏上下文标签
 *
 * 此方法等同于：
 * @code
 * ribbon->hideContextCategory(this);
 * @endcode
 *
 */
void SARibbonContextCategory::hide()
{
    if (SARibbonBar* ribbon = qobject_cast< SARibbonBar* >(parent())) {
        ribbon->hideContextCategory(this);
    }
}

/**
 * @brief show contextCategory/显示上下文标签
 *
 * 此方法等同于：
 * @code
 * ribbon->showContextCategory(this);
 * @endcode
 */
void SARibbonContextCategory::show()
{
    if (SARibbonBar* ribbon = qobject_cast< SARibbonBar* >(parent())) {
        ribbon->showContextCategory(this);
    }
}

void SARibbonContextCategory::onCategoryTitleChanged(const QString& title)
{
    SARibbonCategory* category = qobject_cast< SARibbonCategory* >(sender());
    if (category) {
        Q_EMIT categoryTitleChanged(category, title);
    }
}

/*** End of inlined file: SARibbonContextCategory.cpp ***/

/*** Start of inlined file: SARibbonGalleryItem.cpp ***/
SARibbonGalleryItem::SARibbonGalleryItem() : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), mAction(nullptr)
{
}

SARibbonGalleryItem::SARibbonGalleryItem(const QString& text, const QIcon& icon)
    : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), mAction(nullptr)
{
    setText(text);
    setIcon(icon);
    setTextAlignment(Qt::AlignTop | Qt::AlignHCenter);
}

SARibbonGalleryItem::SARibbonGalleryItem(QAction* act) : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable)
{
    setTextAlignment(Qt::AlignTop | Qt::AlignHCenter);
    setAction(act);
}

SARibbonGalleryItem::~SARibbonGalleryItem()
{
}

void SARibbonGalleryItem::setData(int role, const QVariant& data)
{
    mDatas[ role ] = data;
}

QVariant SARibbonGalleryItem::data(int role) const
{
    if (mAction) {
        switch (role) {
        case Qt::DisplayRole:
            return (mAction->text());

        case Qt::ToolTipRole:
            return (mAction->toolTip());

        case Qt::DecorationRole:
            return (mAction->icon());
        default:
            break;
        }
    }
    return (mDatas.value(role));
}

void SARibbonGalleryItem::setText(const QString& text)
{
    setData(Qt::DisplayRole, text);
}

QString SARibbonGalleryItem::text() const
{
    if (mAction) {
        return (mAction->text());
    }
    return (data(Qt::DisplayRole).toString());
}

void SARibbonGalleryItem::setToolTip(const QString& text)
{
    setData(Qt::ToolTipRole, text);
}

QString SARibbonGalleryItem::toolTip() const
{
    if (mAction) {
        return (mAction->toolTip());
    }
    return (data(Qt::ToolTipRole).toString());
}

void SARibbonGalleryItem::setIcon(const QIcon& ico)
{
    setData(Qt::DecorationRole, ico);
}

QIcon SARibbonGalleryItem::icon() const
{
    if (mAction) {
        return (mAction->icon());
    }
    return (qvariant_cast< QIcon >(data(Qt::DecorationRole)));
}

bool SARibbonGalleryItem::isSelectable() const
{
    return (mFlags & Qt::ItemIsSelectable);
}

void SARibbonGalleryItem::setSelectable(bool isSelectable)
{
    if (isSelectable) {
        mFlags |= Qt::ItemIsSelectable;
    } else {
        mFlags = (mFlags & (~Qt::ItemIsSelectable));
    }
}

bool SARibbonGalleryItem::isEnable() const
{
    if (mAction) {
        return (mAction->isEnabled());
    }
    return (mFlags & Qt::ItemIsEnabled);
}

void SARibbonGalleryItem::setEnable(bool isEnable)
{
    if (mAction) {
        mAction->setEnabled(isEnable);
    }

    if (isEnable) {
        mFlags |= Qt::ItemIsEnabled;
    } else {
        mFlags = (mFlags & (~Qt::ItemIsEnabled));
    }
}

void SARibbonGalleryItem::setFlags(Qt::ItemFlags flag)
{
    mFlags = flag;
    if (mAction) {
        mAction->setEnabled(flag & Qt::ItemIsEnabled);
    }
}

Qt::ItemFlags SARibbonGalleryItem::flags() const
{
    return (mFlags);
}

void SARibbonGalleryItem::setAction(QAction* act)
{
    mAction = act;
    if (nullptr == mAction) {
        return;
    }
    if (act->isEnabled()) {
        mFlags |= Qt::ItemIsEnabled;
    } else {
        mFlags = (mFlags & (~Qt::ItemIsEnabled));
    }
}

QAction* SARibbonGalleryItem::action()
{
    return (mAction);
}

void SARibbonGalleryItem::setTextAlignment(Qt::Alignment a)
{
    setData(Qt::TextAlignmentRole, (int)a);
}

Qt::Alignment SARibbonGalleryItem::textAlignment() const
{
    return qvariant_cast< Qt::Alignment >(data(Qt::TextAlignmentRole));
}

/*** End of inlined file: SARibbonGalleryItem.cpp ***/

/*** Start of inlined file: SARibbonGalleryGroup.cpp ***/
#include <QPainter>
#include <QDebug>
#include <QActionGroup>
#include <QItemSelectionModel>

/**
 * @brief The SARibbonGalleryGroupPrivate class
 */
class SARibbonGalleryGroup::PrivateData
{
public:
    SARibbonGalleryGroup* q_ptr;
    QString mGroupTitle;
    SARibbonGalleryGroup::GalleryGroupStyle mPreStyle { SARibbonGalleryGroup::IconWithText };
    SARibbonGalleryGroup::DisplayRow mDisplayRow { SARibbonGalleryGroup::DisplayOneRow };
    bool mBlockRecalc { false };
    int mGridMinimumWidth { 0 };             ///< grid最小宽度
    int mGridMaximumWidth { 0 };             ///< grid最大宽度
    QActionGroup* mActionGroup { nullptr };  ///< 所有GalleryGroup管理的actions都由这个actiongroup管理
public:
    PrivateData(SARibbonGalleryGroup* p) : q_ptr(p)
    {
        mActionGroup = new QActionGroup(p);
        p->connect(mActionGroup, &QActionGroup::triggered, p, &SARibbonGalleryGroup::triggered);
        p->connect(mActionGroup, &QActionGroup::hovered, p, &SARibbonGalleryGroup::hovered);
    }
};

//===================================================
// SARibbonGalleryGroupItemDelegate
//===================================================

SARibbonGalleryGroupItemDelegate::SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent)
    : QStyledItemDelegate(parent), mGroup(group)
{
}

SARibbonGalleryGroupItemDelegate::~SARibbonGalleryGroupItemDelegate()
{
}

void SARibbonGalleryGroupItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (nullptr == mGroup) {
        return;
    }
    switch (mGroup->galleryGroupStyle()) {
    case SARibbonGalleryGroup::IconWithText:
        paintIconWithText(painter, option, index);
        break;
    case SARibbonGalleryGroup::IconWithWordWrapText:
        paintIconWithTextWordWrap(painter, option, index);
        break;
    case SARibbonGalleryGroup::IconOnly:
        paintIconOnly(painter, option, index);
        break;
    default:
        paintIconWithText(painter, option, index);
        break;
    }
}

void SARibbonGalleryGroupItemDelegate::paintIconOnly(QPainter* painter,
                                                     const QStyleOptionViewItem& option,
                                                     const QModelIndex& index) const
{
    QStyle* style = mGroup->style();
    int sp        = mGroup->spacing();
    sp += 3;
    painter->save();
    painter->setClipRect(option.rect);
    style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, mGroup);
    // draw the icon
    QRect iconRect = option.rect;

    iconRect.adjust(sp, sp, -sp, -sp);
    QIcon ico = qvariant_cast< QIcon >(index.data(Qt::DecorationRole));

    ico.paint(painter, iconRect, Qt::AlignCenter, QIcon::Normal, QIcon::On);
    painter->restore();
}

void SARibbonGalleryGroupItemDelegate::paintIconWithText(QPainter* painter,
                                                         const QStyleOptionViewItem& option,
                                                         const QModelIndex& index) const
{
    QStyledItemDelegate::paint(painter, option, index);
}

void SARibbonGalleryGroupItemDelegate::paintIconWithTextWordWrap(QPainter* painter,
                                                                 const QStyleOptionViewItem& option,
                                                                 const QModelIndex& index) const
{
    QStyledItemDelegate::paint(painter, option, index);
}

QSize SARibbonGalleryGroupItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    Q_UNUSED(index);
    Q_UNUSED(option);
    return mGroup->gridSize();
}

//===================================================
// SARibbonGalleryGroupModel
//===================================================

SARibbonGalleryGroupModel::SARibbonGalleryGroupModel(QObject* parent) : QAbstractListModel(parent)
{
}

SARibbonGalleryGroupModel::~SARibbonGalleryGroupModel()
{
    clear();
}

int SARibbonGalleryGroupModel::rowCount(const QModelIndex& parent) const
{
    return (parent.isValid() ? 0 : mItems.size());
}

Qt::ItemFlags SARibbonGalleryGroupModel::flags(const QModelIndex& index) const
{
    if (!index.isValid() || (index.row() >= mItems.size())) {
        return (Qt::NoItemFlags);
    }
    return (mItems.at(index.row())->flags());
}

QVariant SARibbonGalleryGroupModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() || (index.row() >= mItems.count())) {
        return (QVariant());
    }
    return (mItems.at(index.row())->data(role));
}

QModelIndex SARibbonGalleryGroupModel::index(int row, int column, const QModelIndex& parent) const
{
    if (hasIndex(row, column, parent)) {
        return (createIndex(row, column, mItems.at(row)));
    }
    return (QModelIndex());
}

bool SARibbonGalleryGroupModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (!index.isValid() || (index.row() >= mItems.count())) {
        return (false);
    }

    mItems.at(index.row())->setData(role, value);
    return (true);
}

void SARibbonGalleryGroupModel::clear()
{
    beginResetModel();
    for (int i = 0; i < mItems.count(); ++i) {
        if (mItems.at(i)) {
            delete mItems.at(i);
        }
    }
    mItems.clear();
    endResetModel();
}

SARibbonGalleryItem* SARibbonGalleryGroupModel::at(int row) const
{
    return (mItems.value(row));
}

void SARibbonGalleryGroupModel::insert(int row, SARibbonGalleryItem* item)
{
    beginInsertRows(QModelIndex(), row, row);
    mItems.insert(row, item);
    endInsertRows();
}

SARibbonGalleryItem* SARibbonGalleryGroupModel::take(int row)
{
    if ((row < 0) || (row >= mItems.count())) {
        return (0);
    }

    beginRemoveRows(QModelIndex(), row, row);
    SARibbonGalleryItem* item = mItems.takeAt(row);

    endRemoveRows();
    return (item);
}

void SARibbonGalleryGroupModel::append(SARibbonGalleryItem* item)
{
    beginInsertRows(QModelIndex(), mItems.count(), mItems.count() + 1);
    mItems.append(item);
    endInsertRows();
}

//===================================================
// SARibbonGalleryGroup
//===================================================
SARibbonGalleryGroup::SARibbonGalleryGroup(QWidget* w)
    : QListView(w), d_ptr(new SARibbonGalleryGroup::PrivateData(this))
{
    setViewMode(QListView::IconMode);
    setResizeMode(QListView::Adjust);
    setSelectionRectVisible(true);
    setUniformItemSizes(true);
    setSpacing(1);
    setItemDelegate(new SARibbonGalleryGroupItemDelegate(this, this));
    connect(this, &QAbstractItemView::clicked, this, &SARibbonGalleryGroup::onItemClicked);
    SARibbonGalleryGroupModel* m = new SARibbonGalleryGroupModel(this);
    setModel(m);
}

SARibbonGalleryGroup::~SARibbonGalleryGroup()
{
}

/**
 * @brief 是否禁止计算
 * @param on
 */
void SARibbonGalleryGroup::setRecalcGridSizeBlock(bool on)
{
    d_ptr->mBlockRecalc = on;
}

bool SARibbonGalleryGroup::isRecalcGridSizeBlock() const
{
    return d_ptr->mBlockRecalc;
}

/**
 * @brief 重新计算grid和icon的尺寸
 */
void SARibbonGalleryGroup::recalcGridSize()
{
    recalcGridSize(height());
}

void SARibbonGalleryGroup::recalcGridSize(int galleryHeight)
{
    if (isRecalcGridSizeBlock()) {
        return;
    }
    // 首先通过DisplayRow计算GridSize
    int dr = static_cast< int >(displayRow());
    if (dr < 1) {
        dr = 1;
    } else if (dr > 3) {
        dr = 3;
    }
    int h = galleryHeight / dr;
    if (h <= 1) {
        h = galleryHeight;
    }
    int w = h;
    if (gridMinimumWidth() > 0) {
        if (w < gridMinimumWidth()) {
            w = gridMinimumWidth();
        }
    }
    if (gridMaximumWidth() > 0) {
        if (w > gridMaximumWidth()) {
            w = gridMaximumWidth();
        }
    }
    setGridSize(QSize(w, h));
    // 在通过GalleryGroupStyle确定icon的尺寸
    const int shiftpix =
        4;  // 这个是移动像素，qt在鼠标移动到图标上时会移动一下，给用户明确的动态，导致如果布局很满会超出显示范围，因此要在此基础上缩放一点
    switch (galleryGroupStyle()) {
    case IconWithText: {
        int textHeight = fontMetrics().lineSpacing();
        int iconHeight = h - textHeight - 2 * spacing() - shiftpix;
        if (iconHeight > 0) {
            setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight));
        } else {
            setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix));
        }
        break;
    }
    case IconWithWordWrapText: {
        int textHeight = fontMetrics().lineSpacing() * 2;
        int iconHeight = h - textHeight;
        if (iconHeight > 0) {
            setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight - 2 * spacing() - shiftpix));
        } else {
            setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix));
        }
        break;
    }
    case IconOnly: {
        setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix));
        break;
    }
    default: {
        setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix));
        break;
    }
    }
#if 0
	qDebug() << "SARibbonGalleryGroup::recalcGridSize(" << galleryHeight << "): gridSize=" << gridSize()
			 << " iconSize=" << iconSize();
#endif
}

///
/// \brief 设置默认的预设样式
/// \param style
///
void SARibbonGalleryGroup::setGalleryGroupStyle(SARibbonGalleryGroup::GalleryGroupStyle style)
{
    d_ptr->mPreStyle = style;
    if (style == IconWithWordWrapText) {
        setWordWrap(true);
    }
    recalcGridSize();
}

SARibbonGalleryGroup::GalleryGroupStyle SARibbonGalleryGroup::galleryGroupStyle() const
{
    return d_ptr->mPreStyle;
}

void SARibbonGalleryGroup::addItem(const QString& text, const QIcon& icon)
{
    if (nullptr == groupModel()) {
        return;
    }
    addItem(new SARibbonGalleryItem(text, icon));
}

/**
 * @brief 添加一个条目
 *
 * @param item 条目的内存所有权归属SARibbonGalleryGroup管理
 */
void SARibbonGalleryGroup::addItem(SARibbonGalleryItem* item)
{
    if (nullptr == groupModel()) {
        return;
    }
    groupModel()->append(item);
}

void SARibbonGalleryGroup::addActionItem(QAction* act)
{
    if (nullptr == groupModel()) {
        return;
    }
    d_ptr->mActionGroup->addAction(act);
    groupModel()->append(new SARibbonGalleryItem(act));
}

void SARibbonGalleryGroup::addActionItemList(const QList< QAction* >& acts)
{
    SARibbonGalleryGroupModel* model = groupModel();

    if (nullptr == model) {
        return;
    }
    for (QAction* a : acts) {
        d_ptr->mActionGroup->addAction(a);
    }
    for (int i = 0; i < acts.size(); ++i) {
        model->append(new SARibbonGalleryItem(acts[ i ]));
    }
}

///
/// \brief 构建一个model，这个model的父类是SARibbonGalleryGroup，如果要共享model，需要手动处理model的父类
///
void SARibbonGalleryGroup::setupGroupModel()
{
    setModel(new SARibbonGalleryGroupModel(this));
}

SARibbonGalleryGroupModel* SARibbonGalleryGroup::groupModel()
{
    return (qobject_cast< SARibbonGalleryGroupModel* >(model()));
}

void SARibbonGalleryGroup::setGroupTitle(const QString& title)
{
    d_ptr->mGroupTitle = title;
    Q_EMIT groupTitleChanged(d_ptr->mGroupTitle);
}

QString SARibbonGalleryGroup::groupTitle() const
{
    return (d_ptr->mGroupTitle);
}

void SARibbonGalleryGroup::selectByIndex(int i)
{
    SARibbonGalleryGroupModel* model = groupModel();

    if (nullptr == model) {
        return;
    }
    QModelIndex ind               = model->index(i, 0, QModelIndex());
    QItemSelectionModel* selmodel = selectionModel();

    if (selmodel) {
        selmodel->select(ind, QItemSelectionModel::SelectCurrent);
    }
}

/**
 * @brief 设置显示的行数
 * @param r
 */
void SARibbonGalleryGroup::setDisplayRow(DisplayRow r)
{
    d_ptr->mDisplayRow = r;
    recalcGridSize();
}

/**
 * @brief Gallery显示的行数
 * @return
 */
SARibbonGalleryGroup::DisplayRow SARibbonGalleryGroup::displayRow() const
{
    return d_ptr->mDisplayRow;
}

/**
 * @brief 设置grid最小的宽度，默认为0（不限制）
 * @param w
 */
void SARibbonGalleryGroup::setGridMinimumWidth(int w)
{
    d_ptr->mGridMinimumWidth = w;
}

/**
 * @brief grid最小的宽度，默认为0（不限制）
 * @return
 */
int SARibbonGalleryGroup::gridMinimumWidth() const
{
    return d_ptr->mGridMinimumWidth;
}

/**
 * @brief 设置grid最大的宽度，默认为0（不限制）
 * @param w
 */
void SARibbonGalleryGroup::setGridMaximumWidth(int w)
{
    d_ptr->mGridMaximumWidth = w;
}

/**
 * @brief grid最大的的宽度，默认为0（不限制）
 * @param w
 */
int SARibbonGalleryGroup::gridMaximumWidth() const
{
    return d_ptr->mGridMaximumWidth;
}

/**
 * @brief 获取SARibbonGalleryGroup管理的actiongroup
 * @return
 */
QActionGroup* SARibbonGalleryGroup::actionGroup() const
{
    return d_ptr->mActionGroup;
}

void SARibbonGalleryGroup::onItemClicked(const QModelIndex& index)
{
    if (index.isValid()) {
        SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer();
        if (item) {
            QAction* act = item->action();
            if (act) {
                act->activate(QAction::Trigger);
            }
        }
    }
}

void SARibbonGalleryGroup::onItemEntered(const QModelIndex& index)
{
    if (index.isValid()) {
        SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer();
        if (item) {
            QAction* act = item->action();
            if (act) {
                act->activate(QAction::Hover);
            }
        }
    }
}

/*** End of inlined file: SARibbonGalleryGroup.cpp ***/

/*** Start of inlined file: SARibbonGallery.cpp ***/
#include <QIcon>
#include <QApplication>
#define ICON_ARROW_UP QIcon(":/SARibbon/image/resource/ArrowUp.png")
#define ICON_ARROW_DOWN QIcon(":/SARibbon/image/resource/ArrowDown.png")
#define ICON_ARROW_MORE QIcon(":/SARibbon/image/resource/ArrowMore.png")
#include <QResizeEvent>
#include <QDebug>
#include <QVBoxLayout>
#include <QScrollBar>
#include <QLabel>

#include <QActionGroup>

/**
 * @brief The SARibbonGalleryPrivate class
 */
class SARibbonGallery::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonGallery)
public:
    static int sGalleryButtonMaximumWidth;
    SARibbonGalleryButton* mButtonUp { nullptr };
    SARibbonGalleryButton* mButtonDown { nullptr };
    SARibbonGalleryButton* mButtonMore { nullptr };
#if 0
	SARibbonMenu *popupWidget;
#else
    SARibbonGalleryViewport* mPopupWidget { nullptr };
#endif
    SARibbonGalleryGroup* mViewportGroup { nullptr };
    QBoxLayout* mButtonLayout { nullptr };
    QBoxLayout* mLayout { nullptr };
    PrivateData(SARibbonGallery* p) : q_ptr(p)
    {
    }

    void init()
    {
        mButtonUp   = new SARibbonGalleryButton(q_ptr);
        mButtonDown = new SARibbonGalleryButton(q_ptr);
        mButtonMore = new SARibbonGalleryButton(q_ptr);
        mButtonUp->setToolButtonStyle(Qt::ToolButtonIconOnly);
        mButtonDown->setToolButtonStyle(Qt::ToolButtonIconOnly);
        mButtonMore->setToolButtonStyle(Qt::ToolButtonIconOnly);
        mButtonUp->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        mButtonDown->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        mButtonMore->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        mButtonUp->setObjectName(QStringLiteral("SARibbonGalleryButtonUp"));
        mButtonDown->setObjectName(QStringLiteral("SARibbonGalleryButtonDown"));
        mButtonMore->setObjectName(QStringLiteral("SARibbonGalleryButtonMore"));
        mButtonUp->setMaximumWidth(sGalleryButtonMaximumWidth);
        mButtonDown->setMaximumWidth(sGalleryButtonMaximumWidth);
        mButtonMore->setMaximumWidth(sGalleryButtonMaximumWidth);
        mButtonUp->setIcon(ICON_ARROW_UP);
        mButtonDown->setIcon(ICON_ARROW_DOWN);
        mButtonMore->setIcon(ICON_ARROW_MORE);
        q_ptr->connect(mButtonUp, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageUp);
        q_ptr->connect(mButtonDown, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageDown);
        q_ptr->connect(mButtonMore, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::showMoreDetail);
        // 信号转发
        q_ptr->connect(q_ptr, &SARibbonGallery::triggered, q_ptr, &SARibbonGallery::onTriggered);
        mPopupWidget   = nullptr;
        mViewportGroup = nullptr;
        mButtonLayout  = new QBoxLayout(QBoxLayout::TopToBottom);
        mButtonLayout->setSpacing(0);
        mButtonLayout->setContentsMargins(0, 0, 0, 0);
        mButtonLayout->addWidget(mButtonUp);
        mButtonLayout->addWidget(mButtonDown);
        mButtonLayout->addWidget(mButtonMore);
        mLayout = new QBoxLayout(QBoxLayout::RightToLeft);
        mLayout->setSpacing(0);
        mLayout->setContentsMargins(0, 0, 0, 0);
        mLayout->addLayout(mButtonLayout);
        mLayout->addStretch();
        q_ptr->setLayout(mLayout);
    }

    bool isValid() const
    {
        return (q_ptr != nullptr);
    }

    void createPopupWidget()
    {
        if (nullptr == mPopupWidget) {
#if 0
			popupWidget = new SARibbonMenu(Parent);
#else
            mPopupWidget = new SARibbonGalleryViewport(q_ptr);
#endif
        }
    }

    void setViewPort(SARibbonGalleryGroup* v)
    {
        if (nullptr == mViewportGroup) {
            mViewportGroup = RibbonSubElementFactory->createRibbonGalleryGroup(q_ptr);
            mLayout->addWidget(mViewportGroup, 1);
        }
        mViewportGroup->setRecalcGridSizeBlock(true);
        mViewportGroup->setGalleryGroupStyle(v->galleryGroupStyle());
        mViewportGroup->setDisplayRow(v->displayRow());
        mViewportGroup->setSpacing(v->spacing());
        mViewportGroup->setGridMaximumWidth(v->gridMaximumWidth());
        mViewportGroup->setGridMinimumWidth(v->gridMinimumWidth());
        mViewportGroup->setRecalcGridSizeBlock(false);
        mViewportGroup->recalcGridSize(mViewportGroup->height());
        mViewportGroup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        mViewportGroup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        mViewportGroup->setModel(v->model());
        mViewportGroup->show();
    }
};

// 静态变量初始化

/**
 * @brief SARibbonGalleryPrivate::sGalleryButtonMaximumWidth
 */
int SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = 15;

//===================================================
// SARibbonGalleryButton
//===================================================

SARibbonGalleryButton::SARibbonGalleryButton(QWidget* parent) : QToolButton(parent)
{
}

SARibbonGalleryButton::~SARibbonGalleryButton()
{
}
//===================================================
// SARibbonGalleryViewport
//===================================================

SARibbonGalleryViewport::SARibbonGalleryViewport(QWidget* parent) : QWidget(parent)
{
    setWindowFlags(Qt::Popup);
    QPalette pl = palette();
    pl.setBrush(QPalette::Window, pl.brush(QPalette::Base));
    setPalette(pl);
    m_layout = new QVBoxLayout(this);
    m_layout->setSpacing(0);
    m_layout->setContentsMargins(0, 0, 0, 0);
}

/**
 * @brief 添加窗口不带标题
 * @param w
 */
void SARibbonGalleryViewport::addWidget(QWidget* w)
{
    w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
    m_layout->addWidget(w);
}

/**
 * @brief 添加窗口，带标题
 * @param w
 * @param title
 */
void SARibbonGalleryViewport::addWidget(QWidget* w, const QString& title)
{
    QLabel* label = new QLabel(this);
    label->setText(title);
    label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
    m_layout->addWidget(label);
    w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
    m_layout->addWidget(w);
    _widgetToTitleLable[ w ] = label;
}

/**
 * @brief 通过SARibbonGalleryGroup获取对应的标题，用户可以通过此函数设置QLabel的属性
 * @param g
 * @return 如果没有管理group，将返回nullptr
 */
QLabel* SARibbonGalleryViewport::titleLabel(QWidget* w)
{
    return _widgetToTitleLable.value(w, nullptr);
}

/**
 * @brief 移除窗口
 * @param w
 */
void SARibbonGalleryViewport::removeWidget(QWidget* w)
{
    QLabel* label = titleLabel(w);
    if (label) {
        m_layout->removeWidget(label);
    }
    m_layout->removeWidget(w);
}

/**
 * @brief widget的标题改变
 * @param g
 * @param title
 */
void SARibbonGalleryViewport::onTitleChanged(QWidget* w, const QString& title)
{
    QLabel* l = titleLabel(w);
    if (l) {
        l->setText(title);
    }
}

//===================================================
// SARibbonGallery
//===================================================

SARibbonGallery::SARibbonGallery(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonGallery::PrivateData(this))
{
    d_ptr->init();
    setFrameShape(QFrame::Box);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setMinimumWidth(200);
}

SARibbonGallery::~SARibbonGallery()
{
}

QSize SARibbonGallery::sizeHint() const
{
    return (QSize(100, 62));
}

/**
 * @brief 获取一个空白SARibbonGalleryGroup
 * @return
 */
SARibbonGalleryGroup* SARibbonGallery::addGalleryGroup()
{
    SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this);
    addGalleryGroup(group);
    return (group);
}

/**
 * @brief 添加一组SARibbonGalleryGroup
 * @param group
 */
void SARibbonGallery::addGalleryGroup(SARibbonGalleryGroup* group)
{
    group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    SARibbonGalleryViewport* viewport = ensureGetPopupViewPort();
    viewport->addWidget(group, group->groupTitle());
    connect(group, &QAbstractItemView::clicked, this, &SARibbonGallery::onItemClicked);
    connect(group, &SARibbonGalleryGroup::groupTitleChanged, this, [ group, viewport ](const QString& t) {
        viewport->onTitleChanged(group, t);
    });
    connect(group, &SARibbonGalleryGroup::triggered, this, &SARibbonGallery::triggered);
    connect(group, &SARibbonGalleryGroup::hovered, this, &SARibbonGallery::hovered);
    setCurrentViewGroup(group);
}

/**
 * @brief 添加一组actions
 * @param title actions组的名字
 * @param actions
 * @return 返回SARibbonGalleryGroup，用户可以通过修改SARibbonGalleryGroup属性控制其显示方法
 */
SARibbonGalleryGroup* SARibbonGallery::addCategoryActions(const QString& title, QList< QAction* > actions)
{
    SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this);
    if (!title.isEmpty()) {
        group->setGroupTitle(title);
    }
    group->addActionItemList(actions);
    addGalleryGroup(group);
    return (group);
}

void SARibbonGallery::setCurrentViewGroup(SARibbonGalleryGroup* group)
{
    d_ptr->setViewPort(group);
    QApplication::postEvent(this, new QResizeEvent(size(), size()));
}

SARibbonGalleryGroup* SARibbonGallery::currentViewGroup() const
{
    return (d_ptr->mViewportGroup);
}

/**
 * @brief 获取弹出窗口
 * @return
 */
SARibbonGalleryViewport* SARibbonGallery::getPopupViewPort() const
{
    return d_ptr->mPopupWidget;
}

/**
 * @brief 设置最右边三个控制按钮的最大宽度（默认15）
 * @param w
 */
void SARibbonGallery::setGalleryButtonMaximumWidth(int w)
{
    SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = w;
}

/**
 * @brief 上翻页
 */
void SARibbonGallery::pageDown()
{
    if (d_ptr->mViewportGroup) {
        QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar();
        int v                  = vscrollBar->value();
        v += vscrollBar->singleStep();
        vscrollBar->setValue(v);
    }
}

/**
 * @brief 下翻页
 */
void SARibbonGallery::pageUp()
{
    if (d_ptr->mViewportGroup) {
        QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar();
        int v                  = vscrollBar->value();
        v -= vscrollBar->singleStep();
        vscrollBar->setValue(v);
    }
}

/**
 * @brief 显示更多触发，默认弹出内部管理的SARibbonGalleryViewport，用户可重载此函数实现自定义的弹出
 */
void SARibbonGallery::showMoreDetail()
{
    if (nullptr == d_ptr->mPopupWidget) {
        return;
    }
    QSize popupMenuSize = d_ptr->mPopupWidget->sizeHint();
    QPoint start        = mapToGlobal(QPoint(0, 0));

    int width = d_ptr->mViewportGroup->width();  // viewport

    width += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);  // scrollbar
    d_ptr->mPopupWidget->setGeometry(start.x(), start.y(), width, popupMenuSize.height());
    d_ptr->mPopupWidget->show();
}

void SARibbonGallery::onItemClicked(const QModelIndex& index)
{
    QObject* obj                = sender();
    SARibbonGalleryGroup* group = qobject_cast< SARibbonGalleryGroup* >(obj);
    if (group) {
        setCurrentViewGroup(group);
        SARibbonGalleryGroup* curGroup = currentViewGroup();
        curGroup->scrollTo(index);
        curGroup->setCurrentIndex(index);
    }
}

void SARibbonGallery::onTriggered(QAction* action)
{
    Q_UNUSED(action);
    // 点击后关闭弹出窗口
    if (d_ptr->mPopupWidget) {
        if (d_ptr->mPopupWidget->isVisible()) {
            d_ptr->mPopupWidget->hide();
        }
    }
}

SARibbonGalleryViewport* SARibbonGallery::ensureGetPopupViewPort()
{
    if (nullptr == d_ptr->mPopupWidget) {
        d_ptr->createPopupWidget();
    }
    return (d_ptr->mPopupWidget);
}

void SARibbonGallery::resizeEvent(QResizeEvent* event)
{
    QFrame::resizeEvent(event);
    // 对SARibbonGalleryViewport所有SARibbonGalleryGroup重置尺寸
    int h = layout()->contentsRect().height();
    if (d_ptr->mViewportGroup) {
        h = d_ptr->mViewportGroup->height();
        d_ptr->mViewportGroup->recalcGridSize();
    }
    if (d_ptr->mPopupWidget) {
        QLayout* lay = d_ptr->mPopupWidget->layout();
        if (!lay) {
            return;
        }
        int c = lay->count();
        for (int i = 0; i < c; ++i) {
            QLayoutItem* item = lay->itemAt(i);
            if (!item) {
                continue;
            }
            QWidget* w = item->widget();
            if (!w) {
                continue;
            }
            SARibbonGalleryGroup* g = qobject_cast< SARibbonGalleryGroup* >(w);
            if (!g) {
                continue;
            }
            g->recalcGridSize(h);
        }
    }
}

void SARibbonGallery::paintEvent(QPaintEvent* event)
{
    QFrame::paintEvent(event);
}

/*** End of inlined file: SARibbonGallery.cpp ***/

/*** Start of inlined file: SARibbonBar.cpp ***/
#include <QPointer>
#include <QAction>
#include <QApplication>
#include <QDebug>
#include <QHoverEvent>
#include <QLinearGradient>
#include <QPainter>
#include <QResizeEvent>
#include <QSet>
#include <QStyleOptionMenuItem>
#include <QTimer>
#include <QVariant>
#include <QDateTime>

#ifndef SARIBBONBAR_DEBUG_PRINT
#define SARIBBONBAR_DEBUG_PRINT 0
#endif
#if SARIBBONBAR_DEBUG_PRINT
#ifndef SARIBBONBAR_HELP_DRAW_RECT
#define SARIBBONBAR_HELP_DRAW_RECT(p, rect)                                                                            \
    do {                                                                                                               \
        p.save();                                                                                                      \
        QPen _pen(Qt::DashDotDotLine);                                                                                 \
        _pen.setColor(QColor(219, 26, 59));                                                                            \
        p.setPen(_pen);                                                                                                \
        p.setBrush(QBrush());                                                                                          \
        p.drawRect(rect);                                                                                              \
        p.restore();                                                                                                   \
    } while (0)
#endif
#endif
class _SAContextCategoryManagerData
{
public:
    SARibbonContextCategory* contextCategory;
    QList< int > tabPageIndex;
    bool operator==(const SARibbonContextCategory* contextPage)
    {
        return (this->contextCategory == contextPage);
    }
};

/**
 * @todo 此处要修改，此方式容易异常
 */
class _SARibbonTabData
{
public:
    SARibbonCategory* category;
    int index;
    _SARibbonTabData() : category(nullptr), index(-1)
    {
    }
};
Q_DECLARE_METATYPE(_SARibbonTabData)

class SARibbonBar::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonBar)
public:
    QPointer< QAbstractButton > mApplicationButton;
    QPointer< SARibbonTabBar > mRibbonTabBar;
    QPointer< SARibbonStackedWidget > mStackedContainerWidget;
    QPointer< SARibbonButtonGroupWidget > mRightButtonGroup;  ///< 在tab bar右边的按钮群
    QPointer< SARibbonQuickAccessBar > mQuickAccessBar;       ///< 快速响应栏
    QPointer< SARibbonTitleIconWidget > mTitleIconWidget;     ///< 标题栏图标

    QAction* mMinimumCategoryButtonAction { nullptr };  ///< 隐藏面板按钮action
    QList< _SAContextCategoryManagerData > mCurrentShowingContextCategory;
    QList< SARibbonContextCategory* > mContextCategoryList;  ///< 存放所有的上下文标签
    QList< _SARibbonTabData > mHidedCategory;
    qint64 mTabBarLastClickTime { 0 };  ///< 记录tabbar点击的上次点击时间戳，tabbar无法区分双击单击，双击必定触发单击，因此需要此把快速单击去掉
    SARibbonBar::RibbonStyles mRibbonStyle { SARibbonBar::RibbonStyleLooseThreeRow };  ///< ribbon的风格
    SARibbonBar::RibbonMode mCurrentRibbonMode { SARibbonBar::NormalRibbonMode };      ///< 记录当前模式
    QList< QColor > mContextCategoryColorList;                                         ///< contextCategory的色系
    int mContextCategoryColorListIndex { -1 };  ///< 记录contextCategory色系索引
    QColor mTitleTextColor;  ///< 标题文字颜色,默认无效，无效的情况下和SARibbonBar的qss:color属性一致
    QColor mTabBarBaseLineColor { QColor(186, 201, 219) };  ///< tabbar 底部会绘制一条线条，定义线条颜色
    QColor mContextCategoryTitleTextColor { Qt::black };    ///< 记录Context category的标题字体颜色
    Qt::Alignment mTitleAligment { Qt::AlignCenter };       ///< 标题对齐方式
    bool mIsTitleVisible { true };                          ///< 标题是否显示
    QBrush mTitleBackgroundBrush { Qt::NoBrush };           ///< 标题的背景颜色
    SARibbonAlignment mRibbonAlignment { SARibbonAlignment::AlignLeft };                     ///< 对齐方式
    SARibbonPanel::PanelLayoutMode mDefaulePanelLayoutMode { SARibbonPanel::ThreeRowMode };  ///< 默认的PanelLayoutMode
    bool mEnableShowPanelTitle { true };                         ///< 是否允许panel的标题栏显示
    int mPanelSpacing { 0 };                                     ///< panel的spacing
    QSize mPanelSmallToolButtonSize { 20, 20 };                  ///< 记录panel的默认图标大小
    QSize mPanelLargeToolButtonSize { 32, 32 };                  ///< 记录panel的大图标尺寸
    SARibbonMainWindowStyles mMainWindowStyle;                   ///< 记录MainWindow的样式
    FpContextCategoryHighlight mFpContextHighlight { nullptr };  ///< 上下文标签高亮
    bool mEnableTabDoubleClickToMinimumMode { true };  ///< 是否允许tab双击激活ribbon的最小化模式
    bool mEnableWordWrap { true };                     ///< 是否允许文字换行
    qreal buttonMaximumAspectRatio { 1.4 };            ///< 按钮的最大宽高比
public:
    PrivateData(SARibbonBar* par) : q_ptr(par)
    {
        mContextCategoryColorList = SARibbonBar::defaultContextCategoryColorList();
        mFpContextHighlight       = [](const QColor& c) -> QColor { return SA::makeColorVibrant(c); };
    }
    void init();
    // 创建QuickAccessBar
    SARibbonQuickAccessBar* createQuickAccessBar();
    // 创建右边工具栏
    SARibbonButtonGroupWidget* createRightButton();
    // 创建标题栏图标
    SARibbonTitleIconWidget* createTitleIconWidget();
    // 创建一个默认的ApplicationButton
    void createDefaultApplicationButton();
    // 设置一个ApplicationButton
    void setApplicationButton(QAbstractButton* btn);

    bool isContainContextCategoryInList(SARibbonContextCategory* contextCategory);

    void setMinimumMode();

    void setNormalMode();

    QColor getContextCategoryColor();

    void updateTabData();
    // 是否使用ribbon边框，如果不是，将影响布局方式
    bool isUseRibbonFrame() const;
    // 计算上下文标签的标题矩形，矩形位置相对于SARibbonBar
    QRect calcContextCategoryTitleRect(const _SAContextCategoryManagerData& contextData);

    // 重新布局
    void relayout();

    // 初始化新的父对象
    void initNewParent(QWidget* par);
};

void SARibbonBar::PrivateData::init()
{
    q_ptr->setNativeMenuBar(false);

    createDefaultApplicationButton();
    mRibbonTabBar = RibbonSubElementFactory->createRibbonTabBar(q_ptr);
    mRibbonTabBar->setObjectName(QStringLiteral("objSARibbonTabBar"));
    mRibbonTabBar->setDrawBase(false);
    q_ptr->connect(mRibbonTabBar.data(), &QTabBar::currentChanged, q_ptr, &SARibbonBar::onCurrentRibbonTabChanged);
    q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabClicked);
    q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarDoubleClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabDoubleClicked);
    q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabMoved, q_ptr, &SARibbonBar::onTabMoved);
    //
    mStackedContainerWidget = RibbonSubElementFactory->createRibbonStackedWidget(q_ptr);
    mStackedContainerWidget->setObjectName(QStringLiteral("objSARibbonStackedContainerWidget"));
    q_ptr->connect(mStackedContainerWidget.data(), &SARibbonStackedWidget::hidWindow, q_ptr, &SARibbonBar::onStackWidgetHided);
    // 捕获事件，在popmode时必须用到
    mStackedContainerWidget->installEventFilter(q_ptr);
    // 快速工具栏
    createQuickAccessBar();
    // 右侧工具栏
    createRightButton();
    // 创建图标窗口
    createTitleIconWidget();
    setNormalMode();

    SARibbonBarLayout* lay = new SARibbonBarLayout(q_ptr);
    q_ptr->setLayout(lay);
    q_ptr->setRibbonStyle(RibbonStyleLooseThreeRow);
    q_ptr->ensurePolished();
}

SARibbonQuickAccessBar* SARibbonBar::PrivateData::createQuickAccessBar()
{
    mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(q_ptr);
    mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar"));
    mQuickAccessBar->setIconSize(QSize(22, 22));
    return mQuickAccessBar.data();
}

SARibbonButtonGroupWidget* SARibbonBar::PrivateData::createRightButton()
{
    mRightButtonGroup = RibbonSubElementFactory->createButtonGroupWidget(q_ptr);
    mRightButtonGroup->setObjectName(QStringLiteral("objSARibbonRightButtonGroup"));
    mRightButtonGroup->setIconSize(QSize(22, 22));
    return mQuickAccessBar.data();
}

SARibbonTitleIconWidget* SARibbonBar::PrivateData::createTitleIconWidget()
{
    mTitleIconWidget = RibbonSubElementFactory->createRibbonTitleIconWidget(q_ptr);
    return mTitleIconWidget.data();
}

void SARibbonBar::PrivateData::createDefaultApplicationButton()
{
    setApplicationButton(RibbonSubElementFactory->createRibbonApplicationButton(q_ptr));
}

void SARibbonBar::PrivateData::setApplicationButton(QAbstractButton* btn)
{
    if (mApplicationButton) {
        mApplicationButton->hide();
        mApplicationButton->deleteLater();
    }
    if (btn) {
        if (btn->parent() != q_ptr) {
            btn->setParent(q_ptr);
        }
        if (btn->objectName().isEmpty()) {
            btn->setObjectName(QStringLiteral("SARibbonApplicationButton"));
        }
        btn->setVisible(true);
        btn->show();
        q_ptr->connect(btn, &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked);
    }
    mApplicationButton = btn;
}

bool SARibbonBar::PrivateData::isContainContextCategoryInList(SARibbonContextCategory* contextCategory)
{
    for (int i = 0; i < mCurrentShowingContextCategory.size(); ++i) {
        if (mCurrentShowingContextCategory[ i ] == contextCategory) {
            return (true);
        }
    }
    return (false);
}

/**
 * @brief 设置最小模式
 */
void SARibbonBar::PrivateData::setMinimumMode()
{
    mCurrentRibbonMode = SARibbonBar::MinimumRibbonMode;
    mStackedContainerWidget->setPopupMode();
    mStackedContainerWidget->setFocusPolicy(Qt::NoFocus);
    mStackedContainerWidget->clearFocus();
    mRibbonTabBar->setFocus();
    mStackedContainerWidget->hide();
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(q_ptr->layout())) {
        lay->resetSize();
        lay->layoutStackedContainerWidget();
    }
}

/**
 * @brief 设置正常模式
 */
void SARibbonBar::PrivateData::setNormalMode()
{
    mCurrentRibbonMode = SARibbonBar::NormalRibbonMode;
    mStackedContainerWidget->setNormalMode();
    mStackedContainerWidget->setFocus();
    mStackedContainerWidget->show();
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(q_ptr->layout())) {
        lay->resetSize();
        lay->layoutStackedContainerWidget();
    }
}

/**
 * @brief 获取ContextCategory的color
 * @return
 */
QColor SARibbonBar::PrivateData::getContextCategoryColor()
{
    if (mContextCategoryColorList.isEmpty()) {
        mContextCategoryColorListIndex = -1;
        return (QColor());
    }
    ++mContextCategoryColorListIndex;
    if ((mContextCategoryColorListIndex >= mContextCategoryColorList.size()) || (mContextCategoryColorListIndex < 0)) {
        mContextCategoryColorListIndex = 0;
    }
    return (mContextCategoryColorList.at(mContextCategoryColorListIndex));
}

/**
 * @brief 更新tab数据
 */
void SARibbonBar::PrivateData::updateTabData()
{
    int tabcount = mRibbonTabBar->count();

    for (int i = 0; i < tabcount; ++i) {
        QVariant var = mRibbonTabBar->tabData(i);
        if (var.isValid()) {
            _SARibbonTabData p = var.value< _SARibbonTabData >();
            p.index            = i;
            mRibbonTabBar->setTabData(i, QVariant::fromValue(p));
        }
    }
    // 刷新完tabdata信息也要接着刷新ContextCategory信息
    for (_SAContextCategoryManagerData& cd : mCurrentShowingContextCategory) {
        cd.tabPageIndex.clear();
        for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) {
            SARibbonCategory* category = cd.contextCategory->categoryPage(i);
            for (int t = 0; t < tabcount; ++t) {
                QVariant v = mRibbonTabBar->tabData(t);
                if (v.isValid()) {
                    _SARibbonTabData d = v.value< _SARibbonTabData >();
                    if (d.category == category) {
                        cd.tabPageIndex.append(t);
                    }
                } else {
                    cd.tabPageIndex.append(-1);
                }
            }
        }
    }
}

/**
 * @brief 获取当前tabbar的样式
 */
bool SARibbonBar::PrivateData::isUseRibbonFrame() const
{
    return mMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseRibbonFrame);
}

QRect SARibbonBar::PrivateData::calcContextCategoryTitleRect(const _SAContextCategoryManagerData& contextData)
{
    QRect contextTitleRect;
    SARibbonTabBar* ribbonTabBar = mRibbonTabBar.data();
    const QMargins border        = q_ptr->contentsMargins();
    const QList< int >& indexs   = contextData.tabPageIndex;
    if (!indexs.isEmpty()) {
        contextTitleRect = ribbonTabBar->tabRect(indexs.first());
        QRect endRect    = ribbonTabBar->tabRect(indexs.last());
        contextTitleRect.setRight(endRect.right());
        contextTitleRect.translate(ribbonTabBar->x(), ribbonTabBar->y());
        contextTitleRect.setHeight(ribbonTabBar->height() - 1);  // 减1像素，避免tabbar基线覆盖
        contextTitleRect -= ribbonTabBar->tabMargin();
        // 把区域顶部扩展到窗口顶部
        contextTitleRect.setTop(border.top());
    }
    return contextTitleRect;
}

void SARibbonBar::PrivateData::relayout()
{
    if (auto lay = q_ptr->layout()) {
        lay->invalidate();
        lay->activate();
#if SARIBBONBAR_DEBUG_PRINT
        qDebug() << "SARibbonBar relayout";
#endif
    }
}

void SARibbonBar::PrivateData::initNewParent(QWidget* par)
{
    if (!par) {
        return;
    }
    q_ptr->connect(par, &QWidget::windowTitleChanged, q_ptr, &SARibbonBar::onWindowTitleChanged);
    q_ptr->connect(par, &QWidget::windowIconChanged, q_ptr, &SARibbonBar::onWindowIconChanged);
    // 父窗口发生了改变，一般就是加入到了MainWindow中，这时要同步父窗口的信息到图标
    SARibbonTitleIconWidget* titleIcon = q_ptr->titleIconWidget();
    if (!titleIcon) {
        return;
    }

    if (q_ptr->isApplicationButtonVerticalExpansion()) {
        titleIcon->hide();
        return;
    }

    // 非垂直展开模式：尝试设置为 MainWindow 的图标
    if (auto* mainwindow = qobject_cast< SARibbonMainWindow* >(par)) {
        int th = q_ptr->titleBarHeight();
        titleIcon->setWindow(mainwindow);
        titleIcon->setIcon(mainwindow->windowIcon());
        titleIcon->setIconSize(QSize(th - 2, th - 2));
        titleIcon->show();
        titleIcon->raise();
    } else {
        titleIcon->hide();
    }
}

//===================================================
// SARibbonBar
//===================================================

/**
 * @brief SARibbonBar构造函数
 * @param parent
 */
SARibbonBar::SARibbonBar(QWidget* parent) : QMenuBar(parent), d_ptr(new SARibbonBar::PrivateData(this))
{
    d_ptr->init();
    d_ptr->initNewParent(parent);
}

SARibbonBar::~SARibbonBar()
{
}

/**
 * @brief 判断样式是否为2行
 * @param s
 * @return 2行返回true，返回false代表当前是3行
 */
bool SARibbonBar::isTwoRowStyle(SARibbonBar::RibbonStyles s)
{
    return s.testFlag(SARibbonBar::RibbonStyleTwoRow);
}

/**
 * @brief 判断样式是否为3行
 * @param s
 * @return 3行返回true，返回false代表当前是2行
 */
bool SARibbonBar::isThreeRowStyle(RibbonStyles s)
{
    return s.testFlag(SARibbonBar::RibbonStyleThreeRow);
}

/**
 * @brief 判断是否是宽松样式
 * @param s
 * @return 宽松样式（带标题栏）返回true，否则就是紧凑样式
 */
bool SARibbonBar::isLooseStyle(SARibbonBar::RibbonStyles s)
{
    return s.testFlag(SARibbonBar::RibbonStyleLoose);
}

/**
 * @brief 判断是否是紧凑样式
 * @param s
 * @return 紧凑样式（不带标题栏）返回true，否则就是宽松样式
 */
bool SARibbonBar::isCompactStyle(RibbonStyles s)
{
    return s.testFlag(SARibbonBar::RibbonStyleCompact);
}

/**
 * @brief 获取版本信息
 * @return {SA_RIBBON_BAR_VERSION_MAJ}.{SA_RIBBON_BAR_VERSION_MIN}.{SA_RIBBON_BAR_VERSION_PAT}
 */
QString SARibbonBar::versionString()
{
    return QString("%1.%2.%3").arg(SA_RIBBON_BAR_VERSION_MAJ).arg(SA_RIBBON_BAR_VERSION_MIN).arg(SA_RIBBON_BAR_VERSION_PAT);
}

/**
@brief 获取默认的上下文标签颜色列表
@return
*/
QList< QColor > SARibbonBar::defaultContextCategoryColorList()
{
    QList< QColor > res;
    res                           //
        << QColor(206, 232, 252)  // 蓝
        << QColor(253, 238, 179)  // 黄
        << QColor(212, 255, 174)  // 绿
        << QColor(255, 196, 214)  // 红
        << QColor(255, 216, 153)  // 橙
        << QColor(255, 224, 243)  // 玫红
        ;
    return res;
}

/**
 * @brief 提供高分屏的支持静态函数
 *
 * @note 此函数需要在main函数，QApplication生成之前调用
 * @code
 * int main(int argc, char* argv[]){
 *    SARibbonBar::initHighDpi();
 *    QApplication a(argc, argv);
 *    ...
 * }
 * @endcode
 */
void SARibbonBar::initHighDpi()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
    // 在有些时候，无法显示分割线，是由于PassThrough导致的
    QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
}

/**
 * @brief 返回applicationButton
 * @return 默认的applicationButton由 @ref SARibbonApplicationButton 生成，通过@ref setApplicationButton 可设置为其他button
 */
QAbstractButton* SARibbonBar::applicationButton()
{
    return (d_ptr->mApplicationButton.data());
}

/**
 * @brief 设置applicationButton,如果想隐藏，可以传入nullptr
 *
 * 默认会有一个SARibbonApplicationButton，如果想取消，可传入nullptr，或者自定义的button也可以传入
 *
 * @note applicationButton的所有权归SARibbonBar所有，不要在外部对applicationButton进行delete操作
 * @param btn applicationButton指针，可以传入@ref SARibbonApplicationButton，
 * SA已经对SARibbonApplicationButton进行了样式设置
 */
void SARibbonBar::setApplicationButton(QAbstractButton* btn)
{
    d_ptr->setApplicationButton(btn);
    d_ptr->relayout();
}

/**
 * @brief 返回tabbar
 * @return SARibbonTabBar指针
 * @sa SARibbonTabBar
 */
SARibbonTabBar* SARibbonBar::ribbonTabBar()
{
    return (d_ptr->mRibbonTabBar);
}

/**
 * @brief 添加一个标签
 * 如果需要删除，直接delete即可，SARibbonBar会对其进行处理
 * @param title 标签名字，默认情况下SARibbonCategory的object name也被设置为title
 * @return 返回一个窗口容器，在Category里可以添加其他控件
 * @sa SARibbonCategory
 */
SARibbonCategory* SARibbonBar::addCategoryPage(const QString& title)
{
    SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this);

    category->setObjectName(title);
    category->setCategoryName(title);

    addCategoryPage(category);
    return (category);
}

/**
 * @brief 添加一个标签
 * @param category
 */
void SARibbonBar::addCategoryPage(SARibbonCategory* category)
{
    if (nullptr == category) {
        return;
    }
    int index = d_ptr->mRibbonTabBar->count();
    insertCategoryPage(category, index);
}

/**
 * @brief qtdesigner专用
 * @param category
 */
void SARibbonBar::addCategoryPage(QWidget* category)
{
    SARibbonCategory* c = qobject_cast< SARibbonCategory* >(category);

    if (c) {
        addCategoryPage(c);
    }
}

/**
 * @brief 添加一个category，category的位置在index，如果当前category数量少于index，将插入到最后
 * @param title category的标题
 * @param index category的位置
 * @return
 */
SARibbonCategory* SARibbonBar::insertCategoryPage(const QString& title, int index)
{
    SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this);

    category->setObjectName(title);
    category->setCategoryName(title);
    insertCategoryPage(category, index);
    return (category);
}

/**
 * @brief 插入一个category
 * @param category SARibbonCategory指针
 * @param index 插入的位置，如果超出范围，将默认插入到最后
 */
void SARibbonBar::insertCategoryPage(SARibbonCategory* category, int index)
{
    if (nullptr == category) {
        return;
    }
    category->setPanelLayoutMode(panelLayoutMode());
    category->setPanelSpacing(panelSpacing());
    category->setPanelLargeIconSize(panelLargeIconSize());
    category->setPanelSmallIconSize(panelSmallIconSize());
    category->setEnableWordWrap(isEnableWordWrap());

    int i = d_ptr->mRibbonTabBar->insertTab(index, category->categoryName());

    _SARibbonTabData tabdata;

    tabdata.category = category;
    tabdata.index    = i;
    d_ptr->mRibbonTabBar->setTabData(i, QVariant::fromValue(tabdata));
    d_ptr->mStackedContainerWidget->insertWidget(index, category);
    // 更新index信息
    d_ptr->updateTabData();
    connect(category, &QWidget::windowTitleChanged, this, &SARibbonBar::onCategoryWindowTitleChanged);
    connect(category, &SARibbonCategory::actionTriggered, this, &SARibbonBar::actionTriggered);
    d_ptr->relayout();
}

/**
 * @brief 通过名字查找Category
 * @param title Category的名字，既标签的标题
 * @return 如果没有找到，将返回nullptr，如果有重名，将返回第一个查询到的名字，因此，尽量避免重名标签
 * @note 由于翻译等原因，可能title会变化，因此如果想通过固定内容查找category，应该使用 @ref categoryByObjectName
 * @see categoryByObjectName
 */
SARibbonCategory* SARibbonBar::categoryByName(const QString& title) const
{
    int c = d_ptr->mStackedContainerWidget->count();

    for (int i = 0; i < c; ++i) {
        SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i));
        if (w) {
            if (w->categoryName() == title) {
                return (w);
            }
        }
    }
    return (nullptr);
}

/**
 * @brief 通过ObjectName查找Category
 * @param objname
 * @return 如果没有找到，将返回nullptr，如果有同样的ObjectName，将返回第一个查询到的名字，因此，尽量避免ObjectName重名
 * @see categoryByName
 */
SARibbonCategory* SARibbonBar::categoryByObjectName(const QString& objname) const
{
    int c = d_ptr->mStackedContainerWidget->count();

    for (int i = 0; i < c; ++i) {
        SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i));
        if (w) {
            if (w->objectName() == objname) {
                return (w);
            }
        }
    }
    return (nullptr);
}

/**
 * @brief 通过索引找到category，如果超过索引范围，会返回nullptr
 * @param index 索引
 * @return 如果超过索引范围，会返回nullptr
 * @note 如果此时有上下文标签，上下文的标签也会返回
 * @note 通过索引查找的category必须是visible状态的category，如果通过@ref hideCategory 隐藏的标签，通过索引是找不到的
 * @note 通过@ref categoryByObjectName 可以找到所有加入过的标签，
 * 如果想得到ribbonbar管理的所有标签，可以通过函数@ref categoryPages 得到
 * @see categoryIndex categoryByObjectName categoryByName
 */
SARibbonCategory* SARibbonBar::categoryByIndex(int index) const
{
    QVariant var = d_ptr->mRibbonTabBar->tabData(index);

    if (var.isValid()) {
        _SARibbonTabData p = var.value< _SARibbonTabData >();
        return (p.category);
    }
    return (nullptr);
}

/**
 * @brief 隐藏category,并不会删除或者取走，只是隐藏
 * @param category
 */
void SARibbonBar::hideCategory(SARibbonCategory* category)
{
    int tabcount = d_ptr->mRibbonTabBar->count();

    for (int i = 0; i < tabcount; ++i) {
        QVariant var = d_ptr->mRibbonTabBar->tabData(i);
        if (var.isValid()) {
            _SARibbonTabData p = var.value< _SARibbonTabData >();
            if (p.category == category) {
                d_ptr->mHidedCategory.append(p);
                d_ptr->mRibbonTabBar->removeTab(i);  // 仅仅把tab移除
                // 注意Category隐藏后，contex的位置就会发生变化，需要更新
                d_ptr->updateTabData();
                return;
            }
        }
    }
}

/**
 * @brief 显示被隐藏的category
 * @param category
 */
void SARibbonBar::showCategory(SARibbonCategory* category)
{
    for (auto i = d_ptr->mHidedCategory.begin(); i != d_ptr->mHidedCategory.end(); ++i) {
        if (i->category == category) {
            // 说明要显示
            int index = d_ptr->mRibbonTabBar->insertTab(i->index, i->category->categoryName());
            i->index  = index;
            d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(*i));
            d_ptr->mHidedCategory.erase(i);  // 移除
            // 更新index信息
            d_ptr->updateTabData();
            raiseCategory(category);
            return;
        }
    }
    raiseCategory(category);
}

/**
 * @brief 判断这个category是否在显示状态，也就是tabbar有这个category
 * @param category
 * @return
 */
bool SARibbonBar::isCategoryVisible(const SARibbonCategory* c) const
{
    int tabindex = categoryIndex(c);

    return (tabindex >= 0);
}

/**
 * @brief 获取category的索引
 * @param c
 * @return 如果找不到，返回-1
 */
int SARibbonBar::categoryIndex(const SARibbonCategory* c) const
{
    // category的顺序不能以stackedwidget为准，因为存在contextcategory，contextcategory正常是不显示的
    int tabcount = d_ptr->mRibbonTabBar->count();

    for (int i = 0; i < tabcount; ++i) {
        QVariant var = d_ptr->mRibbonTabBar->tabData(i);
        if (var.isValid()) {
            _SARibbonTabData p = var.value< _SARibbonTabData >();
            if (p.category == c) {
                return (i);
            }
        }
    }
    return (-1);
}

/**
 * @brief 移动一个Category从from index到to index
 * @param from
 * @param to
 */
void SARibbonBar::moveCategory(int from, int to)
{
    d_ptr->mRibbonTabBar->moveTab(from, to);
    // 这时要刷新所有tabdata的index信息
    d_ptr->updateTabData();
    // 这里会触发tabMoved信号，在tabMoved信号中调整stacked里窗口的位置
}

/**
 * @brief 获取当前显示的所有的SARibbonCategory，包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回
 *
 * @return
 */
QList< SARibbonCategory* > SARibbonBar::categoryPages(bool getAll) const
{
    int c = d_ptr->mStackedContainerWidget->count();
    QList< SARibbonCategory* > res;

    for (int i = 0; i < c; ++i) {
        SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i));
        if (w) {
            if (!getAll && w->isContextCategory()) {
                // 不是getall且是上下文时跳过
                continue;
            }
            res.append(w);
        }
    }
    return (res);
}

/**
 * @brief 移除SARibbonCategory
 *
 * SARibbonBar不会delete SARibbonCategory*，但这个SARibbonCategory会脱离SARibbonBar的管理
 * 表现在tabbar会移除，面板会移除，使用此函数后可以对SARibbonCategory进行delete
 * @param category
 */
void SARibbonBar::removeCategory(SARibbonCategory* category)
{
    int index     = tabIndex(category);
    bool isupdate = false;
    if (index >= 0) {
        d_ptr->mRibbonTabBar->removeTab(index);
        isupdate = true;
    }
    d_ptr->mStackedContainerWidget->removeWidget(category);
    // 同时验证这个category是否是contexcategory里的

    for (SARibbonContextCategory* c : qAsConst(d_ptr->mContextCategoryList)) {
        c->takeCategory(category);
    }
    // 这时要刷新所有tabdata的index信息
    if (isupdate) {
        d_ptr->updateTabData();
    }
    // 移除完后需要重新布局
    layout()->activate();
}

/**
 * @brief 添加上下文标签
 *
 * 上下文标签是特殊时候触发的标签，需要用户手动触发
 *
 * 调用@ref SARibbonContextCategory::addCategoryPage 可在上下文标签中添加SARibbonCategory，
 * 在上下文标签添加的SARibbonCategory，只有在上下文标签显示的时候才会显示
 * @param title 上下文标签的标题，在Office模式下会显示，在wps模式下不显示。默认情况下SARibbonContextCategory的object name也被设置为title
 * @param color 上下文标签的颜色，如果指定为空QColor(),将会使用SARibbonBar的默认色系
 * @param id 上下文标签的id，以便进行查找
 * @return 返回上下文标签指针
 * @note SARibbonBar拥有SARibbonContextCategory的管理权，用户避免在外部直接delete,如果要删除，调用@ref destroyContextCategory 函数
 */
SARibbonContextCategory* SARibbonBar::addContextCategory(const QString& title, const QColor& color, const QVariant& id)
{
    SARibbonContextCategory* context = RibbonSubElementFactory->createRibbonContextCategory(this);

    context->setObjectName(title);
    context->setContextTitle(title);
    context->setId(id);
    context->setContextColor(color.isValid() ? color : d_ptr->getContextCategoryColor());
    addContextCategory(context);
    return (context);
}

/**
 * @brief 添加上下文标签
 * @param context
 */
void SARibbonBar::addContextCategory(SARibbonContextCategory* context)
{
    if (nullptr == context) {
        return;
    }
    connect(context, &SARibbonContextCategory::categoryPageAdded, this, &SARibbonBar::onContextsCategoryPageAdded);
    connect(context, &SARibbonContextCategory::categoryTitleChanged, this, &SARibbonBar::onContextsCategoryCategoryNameChanged);
    // remove并没有绑定，主要是remove后在stacked里也不会显示，remove且delete的话，stacked里也会删除
    // 如果当前SARibbonContextCategory有category，先插入stackwidget中
    for (int i = 0; i < context->categoryCount(); ++i) {
        if (SARibbonCategory* category = context->categoryPage(i)) {
            d_ptr->mStackedContainerWidget->addWidget(category);
        }
    }
    d_ptr->mContextCategoryList.append(context);
}

/**
 * @brief 显示上下文标签
 * @param context 上下文标签指针
 */
void SARibbonBar::showContextCategory(SARibbonContextCategory* context)
{
    if (isContextCategoryVisible(context)) {
        return;
    }
    _SAContextCategoryManagerData contextCategoryData;

    contextCategoryData.contextCategory = context;
    for (int i = 0; i < context->categoryCount(); ++i) {
        SARibbonCategory* category = context->categoryPage(i);
        // 此句如果模式重复设置不会进行多余操作
        category->setPanelLayoutMode(d_ptr->mDefaulePanelLayoutMode);
        // 切换模式后会改变高度，上下文标签显示时要保证显示出来
        int index = d_ptr->mRibbonTabBar->addTab(category->categoryName());
        contextCategoryData.tabPageIndex.append(index);

        _SARibbonTabData tabdata;
        tabdata.category = category;
        tabdata.index    = index;
        d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(tabdata));
    }
    d_ptr->mCurrentShowingContextCategory.append(contextCategoryData);
    // 由于上下文都是在最后追加，不需要调用updateTabData();

    d_ptr->relayout();

    // 重新布局完后需要重绘,因为上下文标签涉及窗口的绘制
    update();
}

/**
 * @brief 隐藏上下文标签
 * @param context 上下文标签指针
 */
void SARibbonBar::hideContextCategory(SARibbonContextCategory* context)
{
    bool needResize = false;

    for (int i = 0; i < d_ptr->mCurrentShowingContextCategory.size(); ++i) {
        if (d_ptr->mCurrentShowingContextCategory[ i ].contextCategory == context) {
            const QList< int >& indexs = d_ptr->mCurrentShowingContextCategory[ i ].tabPageIndex;
            for (int j = indexs.size() - 1; j >= 0; --j) {
                d_ptr->mRibbonTabBar->removeTab(indexs[ j ]);
            }
            // 注意，再删除ContextCategory后，tab的序号就会改变，这时，这个tab后面的都要调整它的序号
            needResize = true;
            d_ptr->mCurrentShowingContextCategory.removeAt(i);
            break;
        }
    }
    if (needResize) {
        d_ptr->updateTabData();
        d_ptr->relayout();
        // 重新布局完后需要重绘
        update();
    }
}

/**
 * @brief 判断上下文是否在显示状态
 * @param context
 * @return 在显示状态返回true
 * @sa setContextCategoryVisible
 */
bool SARibbonBar::isContextCategoryVisible(SARibbonContextCategory* context)
{
    return (d_ptr->isContainContextCategoryInList(context));
}

/**
 * @brief 设置上下文标签的显示状态
 *
 * 上下文标签的当前显示状态可通过 @ref isContextCategoryVisible 进行判断
 * @param context 上下文标签
 * @param visible 显示状态，true为显示
 */
void SARibbonBar::setContextCategoryVisible(SARibbonContextCategory* context, bool visible)
{
    if (nullptr == context) {
        return;
    }
    if (visible) {
        showContextCategory(context);
    } else {
        hideContextCategory(context);
    }
}

/**
 * @brief 获取所有的上下文标签
 * @return 返回上下文标签列表
 */
QList< SARibbonContextCategory* > SARibbonBar::contextCategoryList() const
{
    return (d_ptr->mContextCategoryList);
}

/**
 * @brief 销毁上下文标签，上下文标签的SARibbonCategory也会随之销毁
 * @param context 需要销毁的上下文标签指针
 */
void SARibbonBar::destroyContextCategory(SARibbonContextCategory* context)
{
    if (nullptr == context) {
        return;
    }
    //! 1、如果上下文标签显示中，先隐藏
    if (isContextCategoryVisible(context)) {
        hideContextCategory(context);
    }
    //! 2、删除上下文标签的相关内容
    d_ptr->mContextCategoryList.removeAll(context);
    //!
    QList< SARibbonCategory* > res = context->categoryList();

    for (SARibbonCategory* c : qAsConst(res)) {
        c->hide();
        c->deleteLater();
    }
    context->deleteLater();
    d_ptr->relayout();
}

/**
 * @brief 获取当前可见的上下文标签的tab索引
 * @return 返回的索引是经过排序且去重的
 */
QList< int > SARibbonBar::currentVisibleContextCategoryTabIndexs() const
{
    QList< int > res;
    for (const _SAContextCategoryManagerData& data : qAsConst(d_ptr->mCurrentShowingContextCategory)) {
        res += data.tabPageIndex;
    }
    if (res.size() > 1) {
        // 1. 排序
        std::sort(res.begin(), res.end());
        // 2. 去重
        auto last = std::unique(res.begin(), res.end());
        res.erase(last, res.end());
    }
    return res;
}

/**
 * @brief 设置为最小/正常模式
 *
 * 隐藏模式下，只会显示tabbar，不会显示内容，默认状态是显示模式
 *
 * 默认下双击tabbar会切换隐藏显示模式，如果想禁用此功能，可以重载 @ref onCurrentRibbonTabDoubleClicked
 * 函数，不对函数进行任何处理即可
 *
 * @param isMinimum 参数为true时，切换为Minimum模式
 * @see 此函数会改变@ref RibbonState 状态，通过@ref currentRibbonState 函数可以查看当前状态
 */
void SARibbonBar::setMinimumMode(bool isMinimum)
{
#if SARIBBONBAR_DEBUG_PRINT
    qDebug() << "SARibbonBar::setHideMode " << isMinimum;
#endif
    if (isMinimum) {
        d_ptr->setMinimumMode();
    } else {
        d_ptr->setNormalMode();
    }
    // 发射信号
    Q_EMIT ribbonModeChanged(isMinimum ? MinimumRibbonMode : NormalRibbonMode);
}

///
/// \brief 当前ribbon是否是隐藏模式
/// \return
///
bool SARibbonBar::isMinimumMode() const
{
    return (d_ptr->mStackedContainerWidget->isPopupMode());
}

/**
 * @brief 显示隐藏ribbon的按钮
 * @param isShow
 */
void SARibbonBar::showMinimumModeButton(bool isShow)
{
    SA_D(d);
    if (isShow && !(d->mMinimumCategoryButtonAction)) {
        activeRightButtonGroup();

        d->mMinimumCategoryButtonAction = new QAction(this);
        d->mMinimumCategoryButtonAction->setIcon(style()->standardIcon(
            isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr));
        connect(d->mMinimumCategoryButtonAction, &QAction::triggered, this, [ this, d ]() {
            this->setMinimumMode(!isMinimumMode());
            d->mMinimumCategoryButtonAction->setIcon(style()->standardIcon(
                isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr));
        });
        if (d->mRightButtonGroup) {
            d->mRightButtonGroup->addAction(d->mMinimumCategoryButtonAction);
        }
    }

    d->mMinimumCategoryButtonAction->setVisible(isShow);
}

/**
 * @brief 是否显示隐藏ribbon按钮
 * @return
 */
bool SARibbonBar::haveShowMinimumModeButton() const
{
    return (nullptr != d_ptr->mMinimumCategoryButtonAction);
}

/**
@brief 隐藏ribbon对应的action
@return
*/
QAction* SARibbonBar::minimumModeAction() const
{
    return d_ptr->mMinimumCategoryButtonAction;
}

/**
 * @brief 是否允许tab双击后进入ribbon的最小模式
 * @return
 */
bool SARibbonBar::isEnableTabDoubleClickToMinimumMode() const
{
    return d_ptr->mEnableTabDoubleClickToMinimumMode;
}

/**
 * @brief 设置是否允许tab双击后，ribbon进入最小化模式，此属性默认开启
 * @param on
 */
void SARibbonBar::setTabDoubleClickToMinimumMode(bool on) const
{
    d_ptr->mEnableTabDoubleClickToMinimumMode = on;
}

/**
 * @brief 当前ribbon的状态（正常|最小化）
 * @return
 */
SARibbonBar::RibbonMode SARibbonBar::currentRibbonState() const
{
    return (d_ptr->mCurrentRibbonMode);
}

/**
@brief tabBar的高度
@return
*/
int SARibbonBar::tabBarHeight() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->tabBarHeight();
    }
    return -1;
}

/**
 * @brief 设置tabbar的高度
 *
 * 用户调用setTabBarHeight后，将使用用户设定的高度，而不使用自动计算的高度，这时tabbar高度不会跟随字体等信息重新计算
 *
 * @note 注意，在RibbonStyleCompact**模式下，tabbar高度要保证小于等于titlebar高度，否则会显示异常
 * @note 此函数不会自动刷新，如果需要刷新调用此函数后需要调用@ref updateRibbonGeometry
 * @param h
 */
void SARibbonBar::setTabBarHeight(int h, bool resizeByNow)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        int oldHeight = lay->tabBarHeight();
        if (oldHeight == h) {
            return;
        }
        lay->setTabBarHeight(h);
        if (resizeByNow) {
            lay->resetSize();
        }
    }
}

/**
@brief 返回标题栏高度
@sa setTitleBarHeight
@return
*/
int SARibbonBar::titleBarHeight() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->titleBarHeight();
    }
    return -1;
}

/**
@brief 设置标题栏的高度
@sa titleBarHeight
@note 此操作会发射@ref titleBarHeightChanged 信号
@param h
*/
void SARibbonBar::setTitleBarHeight(int h, bool resizeByNow)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        int oldHeight = lay->titleBarHeight();
        if (oldHeight == h) {
            return;
        }
        lay->setTitleBarHeight(h);
        if (resizeByNow) {
            lay->resetSize();
        }
        Q_EMIT titleBarHeightChanged(oldHeight, h);
    }
}

/**
 * @brief category的高度
 * @return
 */
int SARibbonBar::categoryHeight() const
{
    return d_ptr->mStackedContainerWidget->height();
}

/**
 * @brief 设置category的高度
 * @param h
 * @param resizeByNow
 */
void SARibbonBar::setCategoryHeight(int h, bool resizeByNow)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        int oldHeight = lay->categoryHeight();
        if (oldHeight == h) {
            return;
        }
        lay->setCategoryHeight(h);
        if (resizeByNow) {
            lay->resetSize();
        }
    }
}

void SARibbonBar::onWindowTitleChanged(const QString& title)
{
    Q_UNUSED(title);
    update();
}

void SARibbonBar::onWindowIconChanged(const QIcon& i)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->setWindowIcon(i);
    }
}

/**
 * @brief category的名字发生改变触发
 * @param title
 */
void SARibbonBar::onCategoryWindowTitleChanged(const QString& title)
{
    // 全部更新一遍
    Q_UNUSED(title);
    updateCategoryTitleToTabName();
}

///
/// \brief ribbon的显示界面隐藏
///
void SARibbonBar::onStackWidgetHided()
{
}

/**
 * @brief 标签切换触发槽函数
 * @param index
 */
void SARibbonBar::onCurrentRibbonTabChanged(int index)
{
    QVariant var               = d_ptr->mRibbonTabBar->tabData(index);
    SARibbonCategory* category = nullptr;

    if (var.isValid()) {
        _SARibbonTabData p = var.value< _SARibbonTabData >();
        category           = p.category;
    }
    if (!category) {
        return;
    }
    if (d_ptr->mStackedContainerWidget->currentWidget() != category) {
        d_ptr->mStackedContainerWidget->setCurrentWidget(category);
    }
    Q_EMIT currentRibbonTabChanged(index);
    if (isMinimumMode()) {
        d_ptr->mRibbonTabBar->clearFocus();
        if (!d_ptr->mStackedContainerWidget->isVisible()) {
            if (d_ptr->mStackedContainerWidget->isPopupMode()) {
                // 在stackedContainerWidget弹出前，先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开
                QHoverEvent ehl(QEvent::HoverLeave,
                                d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()),
                                d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()));
                QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl);
                if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
                    lay->layoutStackedContainerWidget();
                }
                d_ptr->mStackedContainerWidget->setFocus();
                // exec之前先发射信息号，否则会被exec阻塞
                d_ptr->mStackedContainerWidget->show();
            }
        }
    }
}

/**
 * @brief ribbon tab bar单击
 *
 * 此实现必须在eventfilter中传递stackedwidget的QEvent::MouseButtonDblClick事件到tabbar中，否则会导致双击变两次单击
 *
 * 单击事件仅用于响应点击同一个tab时
 * @param index
 */
void SARibbonBar::onCurrentRibbonTabClicked(int index)
{
    const qint64 now = QDateTime::currentMSecsSinceEpoch();
    if (d_ptr->mTabBarLastClickTime > 0 && ((now - d_ptr->mTabBarLastClickTime) < QApplication::doubleClickInterval())) {
        return;
    }
    d_ptr->mTabBarLastClickTime = now;
    if (index != d_ptr->mRibbonTabBar->currentIndex()) {
        // 点击的标签不一致通过changed槽去处理
        return;
    }
    if (isMinimumMode()) {
        if (!d_ptr->mStackedContainerWidget->isVisible()) {
            if (d_ptr->mStackedContainerWidget->isPopupMode()) {
                qDebug() << "QHoverEvent";
                // 在stackedContainerWidget弹出前，先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开
                QHoverEvent ehl(QEvent::HoverLeave,
                                d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()),
                                d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()));
                QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl);
                // 弹出前都调整一下位置，避免移动后位置异常
                if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
                    lay->layoutStackedContainerWidget();
                }
                d_ptr->mStackedContainerWidget->exec();
            }
        }
    }
}

/**
 * @brief ribbon tab bar双击
 *
 * 默认情况下双击会切换最小和正常模式
 * @param index
 */
void SARibbonBar::onCurrentRibbonTabDoubleClicked(int index)
{
    qDebug() << "onCurrentRibbonTabDoubleClicked";
    Q_UNUSED(index);
    d_ptr->mTabBarLastClickTime = QDateTime::currentMSecsSinceEpoch();  // 更新时间
    if (isEnableTabDoubleClickToMinimumMode()) {
        setMinimumMode(!isMinimumMode());
    }
}

void SARibbonBar::onContextsCategoryPageAdded(SARibbonCategory* category)
{
    Q_ASSERT_X(category != nullptr, "onContextsCategoryPageAdded", "add nullptr page");
    d_ptr->mStackedContainerWidget->addWidget(category);  // 这里stackedWidget用append，其他地方都应该使用insert
}

/**
 * @brief 上下文标签管理的标签的名字发生变换
 * @param category
 * @param title
 */
void SARibbonBar::onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title)
{
    Q_UNUSED(category);
    Q_UNUSED(title);
    updateCategoryTitleToTabName();
}

/**
 * @brief 标签移动的信号
 * @param from
 * @param to
 */
void SARibbonBar::onTabMoved(int from, int to)
{
    const QSignalBlocker blocker(d_ptr->mStackedContainerWidget);
    // 调整stacked widget的顺序，调整顺序是为了调用categoryPages函数返回的QList<SARibbonCategory *>顺序和tabbar一致
    d_ptr->mStackedContainerWidget->moveWidget(from, to);
}

/**
 * @brief 根据SARibbonCategory*指针查找tabbar的index
 *
 * @param c SARibbonCategory对应的QObject指针
 * @return 如果没有找到，返回-1
 * @note 此函数不会调用SARibbonCategory*的任何方法，因此可以在SARibbonCategory的destroyed槽中调用
 */
int SARibbonBar::tabIndex(SARibbonCategory* obj)
{
    const int size = d_ptr->mRibbonTabBar->count();

    for (int i = 0; i < size; ++i) {
        QVariant v = d_ptr->mRibbonTabBar->tabData(i);
        if (v.isValid()) {
            _SARibbonTabData innervalue = v.value< _SARibbonTabData >();
            if (innervalue.category == obj) {
                return (i);
            }
        }
    }
    // 如果找不到就从stackedwidget中找

    return (-1);
}

/**
 * @brief 把ribbonbar的内容，同步进各个category中
 * @param autoUpdate
 */
void SARibbonBar::synchronousCategoryData(bool autoUpdate)
{
    iterateCategory([ this ](SARibbonCategory* c) -> bool {
        c->setEnableShowPanelTitle(this->isEnableShowPanelTitle());
        c->setPanelTitleHeight(this->panelTitleHeight());
        c->setCategoryAlignment(this->ribbonAlignment());
        c->setPanelLayoutMode(this->panelLayoutMode());
        return true;
    });
    if (autoUpdate) {
        d_ptr->relayout();
        if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
            // 对category发送布局请求
            lay->layoutCategory();
        }
    }
}

/**
 * @brief 把窗口标题文字转换为显示的文字，这里主要针对[*]占位符进行替换
 * @param title
 * @return 如果存在[*]占位符，则根据父窗口的isWindowModified状态进行文字的替换，isWindowModified为true，[*]替换为*，isWindowModified为false，[*]替换为空
 */
QString SARibbonBar::toDisplayTitleText(const QString& title) const
{
    if (!title.contains("[*]")) {
        return title;
    }
    QWidget* w = parentWidget();
    if (!w) {
        return title;
    }
    QString res = title;
    if (w->isWindowModified()) {
        res.replace("[*]", "*");
    } else {
        res.replace("[*]", "");
    }
    return res;
}

/**
 * @brief 获取标题显示的区域
 * @return
 */
QRect SARibbonBar::getWindowTitleRect() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->titleRect();
    }
    return QRect();
}

/**
@brief 激活tabbar右边的按钮群
@return 返回右边的按钮群指针
*/
SARibbonButtonGroupWidget* SARibbonBar::activeRightButtonGroup()
{
    SA_D(d);
    if (!(d->mRightButtonGroup)) {
        d->createRightButton();
    }
    d->mRightButtonGroup->show();
    return d->mRightButtonGroup.data();
}

/**
@brief 返回右边的按钮群指针
@return 如果没有创建，返回nullptr
*/
SARibbonButtonGroupWidget* SARibbonBar::rightButtonGroup()
{
    return d_ptr->mRightButtonGroup.data();
}

/**
@brief 激活QuickAccessBar
@return
*/
SARibbonQuickAccessBar* SARibbonBar::activeQuickAccessBar()
{
    SARibbonQuickAccessBar* bar = quickAccessBar();
    if (!bar) {
        bar = d_ptr->createQuickAccessBar();
    }
    bar->show();
    return bar;
}

SARibbonQuickAccessBar* SARibbonBar::quickAccessBar()
{
    return (d_ptr->mQuickAccessBar.data());
}

/**
 * @brief 获取标题栏窗口
 * @return
 */
SARibbonTitleIconWidget* SARibbonBar::titleIconWidget() const
{
    return (d_ptr->mTitleIconWidget.data());
}

/**
 * @brief 设置标题图标可见性
 * @param on 是否可见
 */
void SARibbonBar::setTitleIconVisible(bool on)
{
    SARibbonTitleIconWidget* w = titleIconWidget();
    if (w) {
        w->setVisible(on);
    }
}

/**
 * @brief 标题图标是否可见
 * @return
 */
bool SARibbonBar::isTitleIconVisible() const
{
    SARibbonTitleIconWidget* w = titleIconWidget();
    if (w) {
        return w->isVisibleTo(this);
    }
    return false;
}

/**
 * @brief 设置ribbonbar的风格，此函数会重新设置所有元素，包括button的布局方式，
 * 尤其是从三行变到两行的过程，重设的内容较多
 * @note 此函数会自动触发ResizeEvent，不需要手动调用
 * @note 默认情况下2行模式不换行,如果想在两行模式换行，在调用SARibbonBar::setRibbonStyle后，再SARibbonToolButton::setEnableWordWrap(true)
 * ,同时再调用updateRibbonElementGeometry()刷新布局
 * @param v 样式，见@ref SARibbonBar::RibbonStyle
 */
void SARibbonBar::setRibbonStyle(SARibbonBar::RibbonStyles v)
{
    // 先幅值给变量
    d_ptr->mRibbonStyle = v;
#if SARIBBONBAR_DEBUG_PRINT
    qDebug() << "setRibbonStyle(" << v << ")"                //
             << "\n  isThreeRowStyle=" << isThreeRowStyle()  //
             << "\n  isTwoRowStyle=" << isTwoRowStyle()      //
             << "\n  isLooseStyle=" << isLooseStyle()        //
             << "\n  isCompactStyle=" << isCompactStyle()    //
        ;
#endif
    // 执行判断
    setEnableWordWrap(isThreeRowStyle(v));
    setTabOnTitle(isCompactStyle());
    setEnableShowPanelTitle(isThreeRowStyle(v));
    setPanelLayoutMode(isThreeRowStyle(v) ? SARibbonPanel::ThreeRowMode : SARibbonPanel::TwoRowMode);

    // 此函数会调用setFixedHeight
    synchronousCategoryData(false);  // 这里不急着刷新，下面会继续刷新
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->resetSize();
    }

    Q_EMIT ribbonStyleChanged(d_ptr->mRibbonStyle);
}

/**
@brief 返回当前ribbon的风格
@return 返回当前ribbon的风格
*/
SARibbonBar::RibbonStyles SARibbonBar::currentRibbonStyle() const
{
    return (d_ptr->mRibbonStyle);
}

/**
@brief 切换到对应标签
@param index 标签索引
*/
void SARibbonBar::setCurrentIndex(int index)
{
    d_ptr->mRibbonTabBar->setCurrentIndex(index);
    // onCurrentRibbonTabChanged(index);
}

/**
 * @brief 返回当前的tab索引
 * @return 当前的索引
 */
int SARibbonBar::currentIndex()
{
    return (d_ptr->mRibbonTabBar->currentIndex());
}

/**
 * @brief 确保标签显示出来，tab并切换到对应页
 * @param category 标签指针
 */
void SARibbonBar::raiseCategory(SARibbonCategory* category)
{
    for (int i = 0; i < d_ptr->mRibbonTabBar->count(); ++i) {
        _SARibbonTabData p = d_ptr->mRibbonTabBar->tabData(i).value< _SARibbonTabData >();
        if (p.category == category) {
            d_ptr->mRibbonTabBar->setCurrentIndex(i);
            return;
        }
    }
}

/**
 * @brief 判断当前的样式是否为2行
 * @return
 */
bool SARibbonBar::isTwoRowStyle() const
{
    return (d_ptr->mDefaulePanelLayoutMode == SARibbonPanel::TwoRowMode);
}

/**
 * @brief 判断当前的样式是否为3行
 * @return
 */
bool SARibbonBar::isThreeRowStyle() const
{
    return (d_ptr->mDefaulePanelLayoutMode == SARibbonPanel::ThreeRowMode);
}

/**
 * @brief 判断当前的样式是否为宽松样式
 * @return
 */
bool SARibbonBar::isLooseStyle() const
{
    return (SARibbonBar::isLooseStyle(currentRibbonStyle()));
}

/**
 * @brief 判断当前的样式是否为紧凑样式
 * @return
 */
bool SARibbonBar::isCompactStyle() const
{
    return (SARibbonBar::isCompactStyle(currentRibbonStyle()));
}

/**
 * @brief 设置标题的文字颜色
 *
 * 标题是mainwindow的windowTitle，如果要设置标题，直接调用SARibbonMainWindow::setWindowTitle 进行设置
 *
 * 如果不设置标题颜色，默认是SARibbonBar的qss的color属性
 * @param clr
 * @note 此函数不会刷新，刷新请自行调用repaint
 */
void SARibbonBar::setWindowTitleTextColor(const QColor& clr)
{
    d_ptr->mTitleTextColor = clr;
}

/**
 * @brief 获取标题的文字颜色
 * @return 如果返回的是无效颜色，!QColor::isValid()，说明没有手动设置颜色，颜色将跟随SARibbonBar的qss的文字颜色
 */
QColor SARibbonBar::windowTitleTextColor() const
{
    return d_ptr->mTitleTextColor;
}

/**
 * @brief tabbar 底部会绘制一条线条，此接口定义线条颜色
 * @param clr
 */
void SARibbonBar::setTabBarBaseLineColor(const QColor& clr)
{
    d_ptr->mTabBarBaseLineColor = clr;
}

/**
 * @brief 获取tabbar底部基线颜色
 * @return
 */
QColor SARibbonBar::tabBarBaseLineColor() const
{
    return d_ptr->mTabBarBaseLineColor;
}

/**
 * @brief 更新ribbon的布局数据，此函数适用于一些关键性尺寸变化，换起ribbon下面元素的布局
 *
 * @note 此函数调用较慢，避免在高速要求场合调用
 */
void SARibbonBar::updateRibbonGeometry()
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->resetSize();
    }
    iterateCategory([](SARibbonCategory* c) -> bool {
        c->updateItemGeometry();
        return true;
    });
}

/**
 * @brief SARibbonPanel的布局模式
 * @return
 */
SARibbonPanel::PanelLayoutMode SARibbonBar::panelLayoutMode() const
{
    return d_ptr->mDefaulePanelLayoutMode;
}

/**
 * @brief SARibbonPanel的布局模式设置
 * @param m
 */
void SARibbonBar::setPanelLayoutMode(SARibbonPanel::PanelLayoutMode m)
{
    d_ptr->mDefaulePanelLayoutMode = m;
    // 设置布局时，让布局重新计算高度
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->resetSize();
    }
    iterateCategory([ m ](SARibbonCategory* c) -> bool {
        c->setPanelLayoutMode(m);
        return true;
    });
}

/**
 * @brief 设置tab在title上面，这样可以省略title区域
 * @param on
 */
void SARibbonBar::setTabOnTitle(bool on)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->setTabOnTitle(on);
    }
}

/**
 * @brief tab是否在title上面
 * @return
 */
bool SARibbonBar::isTabOnTitle() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->isTabOnTitle();
    }
    return false;
}

/**
 * @brief 设置标题的对齐方式
 * @param al
 */
void SARibbonBar::setWindowTitleAligment(Qt::Alignment al)
{
    d_ptr->mTitleAligment = al;
    update();
}

/**
 * @brief 获取标题的对齐方式
 * @return
 */
Qt::Alignment SARibbonBar::windowTitleAligment() const
{
    return d_ptr->mTitleAligment;
}

/**
 * @brief 设置ribbonbar的按钮文字允许换行
 * @param on
 */
void SARibbonBar::setEnableWordWrap(bool on)
{
    d_ptr->mEnableWordWrap = on;
    iterateCategory([ on ](SARibbonCategory* category) -> bool {
        if (category) {
            category->setEnableWordWrap(on);
        }
        return true;
    });
    updateGeometry();
}

/**
 * @brief 判断是否允许换行
 * @return
 */
bool SARibbonBar::isEnableWordWrap() const
{
    return d_ptr->mEnableWordWrap;
}

/**
 * @brief 设置按钮最大宽高比，这个系数决定按钮的最大宽度
 *
 * 按钮的最大宽度为按钮高度*此系数，例如按钮高度为h，那么按钮最大宽度maxw=h*buttonMaximumAspectRatio
 * 如果在此宽度下文字还无法完全显示，那么按钮将不会继续横向扩展，将使用...替代未完全显示的文字
 *
 * @see buttonMaximumAspectRatio
 */
void SARibbonBar::setButtonMaximumAspectRatio(qreal fac)
{
    d_ptr->buttonMaximumAspectRatio = fac;
    iterateCategory([ fac ](SARibbonCategory* category) -> bool {
        if (category) {
            category->setButtonMaximumAspectRatio(fac);
        }
        return true;
    });
}

/**
 * @brief 按钮最大宽高比，这个系数决定按钮的最大宽度
 * @return 按钮最大宽高比
 * @see setButtonMaximumAspectRatio
 */
qreal SARibbonBar::buttonMaximumAspectRatio() const
{
    return d_ptr->buttonMaximumAspectRatio;
}

/**
 * @brief panel标题栏的高度
 * @return
 */
int SARibbonBar::panelTitleHeight() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->panelTitleHeight();
    }
    return -1;
}

/**
 * @brief 设置panel的高度
 * @param h
 */
void SARibbonBar::setPanelTitleHeight(int h)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->setPanelTitleHeight(h);
    }
    iterateCategory([ h ](SARibbonCategory* c) -> bool {
        c->setPanelTitleHeight(h);
        return true;
    });
}

/**
 * @brief 是否panel显示标题栏
 * @return
 */
bool SARibbonBar::isEnableShowPanelTitle() const
{
    return d_ptr->mEnableShowPanelTitle;
}

/**
 * @brief 设置显示panel标题
 * @param on
 */
void SARibbonBar::setEnableShowPanelTitle(bool on)
{
    d_ptr->mEnableShowPanelTitle = on;
    iterateCategory([ on ](SARibbonCategory* c) -> bool {
        c->setEnableShowPanelTitle(on);
        return true;
    });
}

/**
 * @brief 设置panel的spacing
 * @param n
 */
void SARibbonBar::setPanelSpacing(int n)
{
    d_ptr->mPanelSpacing = n;
    // 同步当前被SARibbonBar管理的SARibbonCategory的PanelSpacing
    iterateCategory([ n ](SARibbonCategory* category) -> bool {
        if (category) {
            category->setPanelSpacing(n);
        }
        return true;
    });
}

/**
 * @brief panel的spacing
 * @return
 */
int SARibbonBar::panelSpacing() const
{
    return d_ptr->mPanelSpacing;
}

/**
 * @brief 设置panel按钮的icon尺寸，large action不受此尺寸影响
 * @param smallSize
 */
void SARibbonBar::setPanelToolButtonIconSize(const QSize& smallSize, const QSize& largeSize)
{
    d_ptr->mPanelSmallToolButtonSize = smallSize;
    d_ptr->mPanelLargeToolButtonSize = largeSize;
    // 同步当前被SARibbonBar管理的SARibbonCategory的PanelSpacing
    iterateCategory([ smallSize, largeSize ](SARibbonCategory* category) -> bool {
        if (category) {
            category->setPanelToolButtonIconSize(smallSize, largeSize);
        }
        return true;
    });
}

/**
 * @brief panel按钮的icon尺寸，large action不受此尺寸影响
 *
 * @note panel按钮是指panel右下角的功能按钮
 * @return
 */
QPair< QSize, QSize > SARibbonBar::panelToolButtonIconSize() const
{
    return qMakePair(d_ptr->mPanelSmallToolButtonSize, d_ptr->mPanelLargeToolButtonSize);
}

/**
 * @brief 设置panel的大按钮图标尺寸
 * @param largeSize
 */
void SARibbonBar::setPanelLargeIconSize(const QSize& largeSize)
{
    d_ptr->mPanelLargeToolButtonSize = largeSize;
    // 同步当前被SARibbonBar管理的SARibbonCategory的PanelSpacing
    iterateCategory([ largeSize ](SARibbonCategory* category) -> bool {
        if (category) {
            category->setPanelLargeIconSize(largeSize);
        }
        return true;
    });
}

/**
 * @brief panel的大按钮图标尺寸
 * @return
 */
QSize SARibbonBar::panelLargeIconSize() const
{
    return d_ptr->mPanelLargeToolButtonSize;
}

/**
 * @brief 设置panel的小按钮图标尺寸
 * @param smallSize
 */
void SARibbonBar::setPanelSmallIconSize(const QSize& smallSize)
{
    d_ptr->mPanelSmallToolButtonSize = smallSize;
    // 同步当前被SARibbonBar管理的SARibbonCategory的PanelSpacing
    iterateCategory([ smallSize ](SARibbonCategory* category) -> bool {
        if (category) {
            category->setPanelSmallIconSize(smallSize);
        }
        return true;
    });
}

/**
 * @brief panel的小按钮图标尺寸
 * @return
 */
QSize SARibbonBar::panelSmallIconSize() const
{
    return d_ptr->mPanelSmallToolButtonSize;
}

/**
 * @brief ribbonbar内部的StackedWidget
 * 所有的category都放置在StackedWidget中
 * @return
 */
SARibbonStackedWidget* SARibbonBar::ribbonStackedWidget()
{
    return d_ptr->mStackedContainerWidget;
}

/**
 * @brief 设置是否显示标题
 * @param on
 */
void SARibbonBar::setTitleVisible(bool on)
{
    d_ptr->mIsTitleVisible = on;
}

/**
 * @brief 判断标题是否显示
 * @return
 */
bool SARibbonBar::isTitleVisible() const
{
    return d_ptr->mIsTitleVisible;
}

/**
 * @brief 设置标题栏的背景颜色
 *
 * 如果不需要颜色，设置Qt::NoBrush
 * @param bk 背景颜色
 */
void SARibbonBar::setWindowTitleBackgroundBrush(const QBrush& bk)
{
    d_ptr->mTitleBackgroundBrush = bk;
}

/**
 * @brief 标题栏的背景颜色
 *
 * 颜色为Qt::NoBrush时，代表没有背景颜色
 * @default Qt::NoBrush
 * @return
 */
QBrush SARibbonBar::windowTitleBackgroundBrush() const
{
    return d_ptr->mTitleBackgroundBrush;
}

/**
@brief 设置上下文标签的颜色列表

上下文标签显示的时候，会从颜色列表中取颜色进行标签的渲染
@param cls
*/
void SARibbonBar::setContextCategoryColorList(const QList< QColor >& cls)
{
    d_ptr->mContextCategoryColorList = cls;
    if (d_ptr->mContextCategoryColorList.isEmpty()) {
        d_ptr->mContextCategoryColorList = defaultContextCategoryColorList();
    }
    d_ptr->mContextCategoryColorListIndex = 0;
    // 这时需要对已经显示的contextCategoryData的颜色进行重新设置
    for (SARibbonContextCategory* c : d_ptr->mContextCategoryList) {
        c->setContextColor(d_ptr->getContextCategoryColor());
    }
}

/**
@brief 获取上下文标签的颜色列表
@return
*/
QList< QColor > SARibbonBar::contextCategoryColorList() const
{
    return d_ptr->mContextCategoryColorList;
}

/**
 * @brief 设置contextCategory 标题的颜色
 * @param clr
 */
void SARibbonBar::setContextCategoryTitleTextColor(const QColor& clr)
{
    d_ptr->mContextCategoryTitleTextColor = clr;
}

/**
 * @brief contextCategory 标题的颜色
 * @return
 */
QColor SARibbonBar::contextCategoryTitleTextColor() const
{
    return d_ptr->mContextCategoryTitleTextColor;
}

/**
 * @brief 设置一个函数指针，函数指针输入上下文标签设定的颜色，输出一个高亮颜色，高亮颜色用于绘制上下文标签的高亮部位，例如最顶部的横线
 * @param fp
 */
void SARibbonBar::setContextCategoryColorHighLight(FpContextCategoryHighlight fp)
{
    d_ptr->mFpContextHighlight = fp;
}

/**
@brief 设置ribbon的对齐方式
@param al
*/
void SARibbonBar::setRibbonAlignment(SARibbonAlignment al)
{
    d_ptr->mRibbonAlignment = al;
    synchronousCategoryData();
}

/**
@brief ribbon的对齐方式
@return
*/
SARibbonAlignment SARibbonBar::ribbonAlignment() const
{
    return d_ptr->mRibbonAlignment;
}

/**
 * @brief 此函数会遍历SARibbonBar下的所有Category，执行函数指针bool(SARibbonCategory*)
 * @param fp 函数指针返回false则停止迭代
 * @return 返回false代表没有迭代完所有的category，中途接收到回调函数的false返回而中断迭代
 */
bool SARibbonBar::iterateCategory(FpCategoryIterate fp) const
{
    const QList< SARibbonCategory* > cs = categoryPages(true);
    for (SARibbonCategory* c : cs) {
        if (!fp(c)) {
            return false;
        }
    }
    return true;
}

/**
 * @brief 此函数会遍历SARibbonBar下的所有Category,并迭代所有的panel，执行函数指针bool(SARibbonPanel*)
 * @param fp 函数指针返回false则停止迭代
 * @return 返回false代表没有迭代完所有的category，中途接收到回调函数的false返回而中断迭代
 */
bool SARibbonBar::iteratePanel(FpPanelIterate fp) const
{
    const QList< SARibbonCategory* > cs = categoryPages(true);
    for (SARibbonCategory* c : cs) {
        if (!c->iteratePanel(fp)) {
            return false;
        }
    }
    return true;
}

/**
 * @brief 设置边角widget可见性，对于mdi窗口，会出现TopLeftCorner和TopRightCorner两个corner widget
 * @param on
 * @param c
 */
void SARibbonBar::setCornerWidgetVisible(bool on, Qt::Corner c)
{
    if (QWidget* w = cornerWidget(c)) {
        w->setVisible(on);
    }
}

/**
 * @brief 设置ApplicationButton垂直方向扩充，这样ApplicationButton能占用标题栏和tab栏两个栏的高度
 * @param on
 */
void SARibbonBar::setApplicationButtonVerticalExpansion(bool on)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->setApplicationButtonVerticalExpansion(on);
        lay->invalidate();
    }
}

/**
 * @brief applicationButton是否是在垂直方向扩充
 *
 * 默认为false
 * @return
 */
bool SARibbonBar::isApplicationButtonVerticalExpansion() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->isApplicationButtonVerticalExpansion();
    }
    return false;
}

/**
 * @brief 此函数会遍历所有panel，并获取它下面的action
 * @return
 */
QList< QAction* > SARibbonBar::allActions() const
{
    QList< QAction* > res;
    auto fp = [ &res ](SARibbonPanel* panel) -> bool {
        res += panel->actions();
        return true;
    };
    iteratePanel(fp);
    return res;
}

/**
 * @brief 判断当前是否使用的是无边框，而不是native边框
 * @return
 */
bool SARibbonBar::isUseRibbonFrame() const
{
    return d_ptr->isUseRibbonFrame();
}

/**
 * @brief SARibbonBar::eventFilter
 * @param obj
 * @param e
 * @return
 */
bool SARibbonBar::eventFilter(QObject* obj, QEvent* e)
{
    if (obj) {
        // 调整多文档时在窗口模式下的按钮更新
        if ((obj == cornerWidget(Qt::TopLeftCorner)) || (obj == cornerWidget(Qt::TopRightCorner))) {
            if ((QEvent::UpdateLater == e->type()) || (QEvent::MouseButtonRelease == e->type())
                || (QEvent::WindowActivate == e->type())) {
                // 这个是多文档系统按钮的更新
                d_ptr->relayout();
            }
        } else if (obj == d_ptr->mStackedContainerWidget) {
            // 在stack 是popup模式时，点击的是stackedContainerWidget区域外的时候，如果是在ribbonTabBar上点击
            // 那么忽略掉这次点击，把点击下发到ribbonTabBar,这样可以避免stackedContainerWidget在点击ribbonTabBar后
            // 隐藏又显示，出现闪烁
            if ((QEvent::MouseButtonPress == e->type()) || (QEvent::MouseButtonDblClick == e->type())) {
                if (d_ptr->mStackedContainerWidget->isPopupMode()) {
                    QMouseEvent* mouseEvent = static_cast< QMouseEvent* >(e);
                    if (!d_ptr->mStackedContainerWidget->rect().contains(mouseEvent->pos())) {
                        QWidget* clickedWidget = QApplication::widgetAt(mouseEvent->globalPos());
                        if (clickedWidget == d_ptr->mRibbonTabBar) {
                            const QPoint targetPoint = clickedWidget->mapFromGlobal(mouseEvent->globalPos());
                            QMouseEvent* evPress     = new QMouseEvent(mouseEvent->type(),
                                                                   targetPoint,
                                                                   mouseEvent->globalPos(),
                                                                   mouseEvent->button(),
                                                                   mouseEvent->buttons(),
                                                                   mouseEvent->modifiers());
                            QApplication::postEvent(clickedWidget, evPress);
                            return (true);
                        }
                    }
                }
            }
        }
    }
    return (QMenuBar::eventFilter(obj, e));
}

/**
 * @brief 根据情况重置tabbar的宽度，主要针对wps模式
 */
int SARibbonBar::calcMinTabBarWidth() const
{
    // 20200831
    // tabBarWidth的宽度原来为endX - x;，现需要根据实际进行调整
    // 为了把tabbar没有tab的部分不占用，这里的宽度需要根据tab的size来进行设置，让tabbar的长度刚刚好，这样能让出
    // mainwindow的空间，接受鼠标事件，从而实现拖动等操作，否则tabbar占用整个顶栏，鼠标无法点击到mainwindow
    // 计算tab所占用的宽度
    const QMargins& mg = d_ptr->mRibbonTabBar->tabMargin();
    return d_ptr->mRibbonTabBar->sizeHint().width() + (mg.left() + mg.right());
}

/**
 * @brief 正常模式下的高度
 *
 * 有可能SARibbonBar::height和mainBarHeight不相等，这种情况发生在RibbonState::MinimumRibbonMode状态下
 * @return 高度
 */
int SARibbonBar::normalModeMainBarHeight() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->normalModeMainBarHeight();
    }
    return -1;
}

/**
@brief 最小模式下的高度
@return
*/
int SARibbonBar::minimumModeMainBarHeight() const
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        return lay->minimumModeMainBarHeight();
    }
    return -1;
}

/**
 * @brief 更新所有的category title对应的tab名
 *
 * 此函数会对所有的category的名字和tab进行匹配，如果匹配不上会重新设置tab名
 */
void SARibbonBar::updateCategoryTitleToTabName()
{
    SARibbonTabBar* tab = d_ptr->mRibbonTabBar;
    for (int i = 0; i < tab->count(); ++i) {
        // 鉴于tab不会很多，不考虑效率问题
        QVariant var = tab->tabData(i);
        if (var.isValid()) {
            _SARibbonTabData p = var.value< _SARibbonTabData >();
            if (p.category && p.category->categoryName() != tab->tabText(i)) {
                tab->setTabText(i, p.category->categoryName());
            }
        }
    }
    repaint();
}

/**
 * @brief 告知WindowButtonGroup的尺寸
 * @param s
 */
void SARibbonBar::setSystemButtonGroupSize(const QSize& s)
{
    if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
        lay->setSystemButtonSize(s);
    }
}

/**
 * @brief 更新标题栏的区域位置
 */
// void SARibbonBar::updateTitleRect()
// {
// 	if (!d_ptr->isUseRibbonFrame()) {
// 		d_ptr->mTitleRect = QRect();
// 	}
// 	SARibbonQuickAccessBar* quickAccessBar = d_ptr->mQuickAccessBar.data();
// 	SARibbonTabBar* ribbonTabBar           = d_ptr->mRibbonTabBar.data();
// 	const QMargins border                  = contentsMargins();
// 	const int validTitleBarHeight          = titleBarHeight();
// 	// 计算标题栏区域
// 	if (isCompactStyle()) {
// 		// 两行紧凑模式
// 		int titleStart = ribbonTabBar->geometry().right();
// 		int titleWidth = quickAccessBar->x() - titleStart;
// 		if (titleWidth > 10) {
// 			d_ptr->mTitleRect = QRect(titleStart, border.top(), titleWidth, validTitleBarHeight);
// 		} else {
// 			d_ptr->mTitleRect = QRect();
// 		}
// 	} else {
// 		// 三行宽松模式
// 		const QList< _SAContextCategoryManagerData >& contextCategoryDataList = d_ptr->mCurrentShowingContextCategory;
// 		// 记录当前上下文标签的最右和最左端
// 		int contextRegionLeft  = width();
// 		int contextRegionRight = -1;
// 		for (const _SAContextCategoryManagerData& contextData : contextCategoryDataList) {
// 			QRect contextTitleRect = d_ptr->calcContextCategoryTitleRect(contextData);
// 			// 更新上下文标签的范围，用于控制标题栏的显示
// 			if (contextTitleRect.left() < contextRegionLeft) {
// 				contextRegionLeft = contextTitleRect.left();
// 			}
// 			if (contextTitleRect.right() > contextRegionRight) {
// 				contextRegionRight = contextTitleRect.right();
// 			}
// 		}
// 		int x1 = border.left();
// 		if (d_ptr->mQuickAccessBar) {
// 			x1 = d_ptr->mQuickAccessBar->geometry().right();
// 			x1 += 1;
// 		}
// 		int x2 = width() - d_ptr->mSystemButtonSize.width() - border.right();
// 		if (contextRegionRight < 0) {
// 			//!说明没有上下文标签，contextRegionRight就是默认值-1

// 			d_ptr->mTitleRect = QRect(QPoint(x1, border.top()), QPoint(x2, validTitleBarHeight + border.top()));
// 		} else {
// 			//! 说明有上下文标签，上下文标签会把标题区域切分为两部分，这时候要找出一个更大的标题区域显示标题
// 			int leftwidth  = contextRegionLeft - x1;   // 计算出上下文标签左边标题栏的宽度
// 			int rightwidth = x2 - contextRegionRight;  // 计算出上下文标签右边标题栏的宽度
// 			if (rightwidth > leftwidth) {
// 				// 说明右边的区域大一点，标题显示在右，显示在右边需要减去windowbutton宽度
// 				d_ptr->mTitleRect =
// 					QRect(QPoint(contextRegionRight, border.top()), QPoint(x2, validTitleBarHeight + border.top()));
// 			} else {
// 				// 说明左边的大一点
// 				d_ptr->mTitleRect =
// 					QRect(QPoint(x1, border.top()), QPoint(contextRegionLeft, validTitleBarHeight + border.top()));
// 			}
// 		}
// 	}
// }

/**
 * @brief SARibbonBar::setMainWindowStyles
 * @param s
 */
void SARibbonBar::setMainWindowStyles(SARibbonMainWindowStyles s)
{
    d_ptr->mMainWindowStyle = s;
}

void SARibbonBar::paintEvent(QPaintEvent* e)
{
    Q_UNUSED(e);
    if (isLooseStyle()) {
        paintInLooseStyle();
    } else {
        paintInCompactStyle();
    }
#if SARIBBONBAR_DEBUG_PRINT
    QPainter p(this);
    if (d_ptr->mQuickAccessBar) {
        SARIBBONBAR_HELP_DRAW_RECT(p, d_ptr->mQuickAccessBar->geometry());
    }
    if (d_ptr->mRibbonTabBar) {
        SARIBBONBAR_HELP_DRAW_RECT(p, d_ptr->mRibbonTabBar->geometry());
    }
    if (d_ptr->mStackedContainerWidget) {
        SARIBBONBAR_HELP_DRAW_RECT(p, d_ptr->mStackedContainerWidget->geometry());
    }
    if (d_ptr->mRightButtonGroup) {
        SARIBBONBAR_HELP_DRAW_RECT(p, d_ptr->mRightButtonGroup->geometry());
    }
#endif
}

void SARibbonBar::paintInLooseStyle()
{
    QPainter p(this);

    //! 1.绘制tabbar下的基线，这个函数仅仅对office2013主题有用，大部分主题都不绘制基线
    paintTabbarBaseLine(p);

    //! 2.显示上下文标签
    const QList< _SAContextCategoryManagerData >& contextCategoryDataList = d_ptr->mCurrentShowingContextCategory;
    for (const _SAContextCategoryManagerData& contextData : contextCategoryDataList) {
        QRect contextTitleRect = d_ptr->calcContextCategoryTitleRect(contextData);
        if (!contextData.tabPageIndex.isEmpty()) {
            // 绘制
            paintContextCategoryTab(p,
                                    contextData.contextCategory->contextTitle(),
                                    contextTitleRect,
                                    contextData.contextCategory->contextColor());
        }
    }

    //! 3.显示标题等
    if (d_ptr->isUseRibbonFrame()) {
        QWidget* parWindow = parentWidget();
        if (parWindow && isTitleVisible()) {
            paintWindowTitle(p, toDisplayTitleText(parWindow->windowTitle()), getWindowTitleRect());
        }
    }
}

void SARibbonBar::paintInCompactStyle()
{
    QPainter p(this);

    //! 1.绘制tabbar下的基线，这个函数仅仅对office2013主题有用，大部分主题都不绘制基线
    paintTabbarBaseLine(p);

    //! 显示上下文标签

    const QList< _SAContextCategoryManagerData >& contextCategoryDataList = d_ptr->mCurrentShowingContextCategory;
    for (const _SAContextCategoryManagerData& contextData : contextCategoryDataList) {
        QRect contextTitleRect = d_ptr->calcContextCategoryTitleRect(contextData);
        if (!contextData.tabPageIndex.isEmpty()) {
            // 绘制
            paintContextCategoryTab(p,
                                    contextData.contextCategory->contextTitle(),
                                    contextTitleRect,
                                    contextData.contextCategory->contextColor());
        }
    }

    //! 显示标题等
    if (d_ptr->isUseRibbonFrame()) {
        QWidget* parWindow = parentWidget();
        if (parWindow && isTitleVisible()) {
            paintWindowTitle(p, toDisplayTitleText(parWindow->windowTitle()), getWindowTitleRect());
        }
    }
}

// void SARibbonBar::resizeStackedContainerWidget()
// {
// 	QMargins border                   = contentsMargins();
// 	const QRect& ribbonTabBarGeometry = d_ptr->mRibbonTabBar->geometry();

// 	int x = border.left();
// 	int y = ribbonTabBarGeometry.bottom() + 1;
// 	int w = width() - border.left() - border.right();
// 	int h = d_ptr->categoryHeight();
// 	if (d_ptr->mStackedContainerWidget->isPopupMode()) {
// 		// 弹出模式时，位置为全局位置
// 		QPoint absPosition = mapToGlobal(QPoint(x, y));
// 		x                  = absPosition.x();
// 		y                  = absPosition.y();
// 	}
// 	d_ptr->mStackedContainerWidget->setFixedSize(QSize(w, h));
// 	d_ptr->mStackedContainerWidget->setGeometry(x, y, w, h);
// }

/**
 * @brief 刷新所有ContextCategoryManagerData，这个在单独一个Category删除时调用
 */
void SARibbonBar::updateContextCategoryManagerData()
{
    const int c = d_ptr->mRibbonTabBar->count();

    for (_SAContextCategoryManagerData& cd : d_ptr->mCurrentShowingContextCategory) {
        cd.tabPageIndex.clear();
        for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) {
            SARibbonCategory* category = cd.contextCategory->categoryPage(i);
            for (int t = 0; t < c; ++t) {
                QVariant v = d_ptr->mRibbonTabBar->tabData(t);
                if (v.isValid()) {
                    _SARibbonTabData d = v.value< _SARibbonTabData >();
                    if (d.category == category) {
                        cd.tabPageIndex.append(t);
                    }
                } else {
                    cd.tabPageIndex.append(-1);
                }
            }
        }
    }
}

/**
 * @brief 绘制上下文标签的背景
 * @param painter 绘图QPainter
 * @param title 上下文标签的title
 * @param contextRect 上下文标签的绘制区域
 * @param color 上下文标签赋予的颜色
 */
void SARibbonBar::paintContextCategoryTab(QPainter& painter, const QString& title, const QRect& contextRect, const QColor& color)
{
    // 绘制上下文标签
    // 首先有5像素的实体粗线位于顶部
    painter.save();
    painter.setPen(Qt::NoPen);
    painter.setBrush(color);
    painter.drawRect(contextRect);
    const int contextLineWidth = 5;
    // 绘制很线
    QColor highlightColor = color;
    if (d_ptr->mFpContextHighlight) {
        highlightColor = d_ptr->mFpContextHighlight(color);
    }
    painter.fillRect(QRect(contextRect.x(), contextRect.y(), contextRect.width(), contextLineWidth), highlightColor);

    // 只有在office模式下才需要绘制标题
    if (isLooseStyle()) {
        if (!title.isEmpty()) {
            QRect textRect = QRect(contextRect.x(),
                                   contextRect.y() + contextLineWidth,
                                   contextRect.width(),
                                   contextRect.height() - contextLineWidth - d_ptr->mRibbonTabBar->height());
            painter.setPen(contextCategoryTitleTextColor());
            QFontMetrics fontMetrics(painter.font());
            painter.drawText(textRect, Qt::AlignCenter, fontMetrics.elidedText(title, Qt::ElideRight, textRect.width()));
        }
    }
    painter.restore();
}

/**
 * @brief 重写moveevent是为了在移动时调整isPopupMode状态下的stackedContainerWidget位置
 * @param event
 */
void SARibbonBar::moveEvent(QMoveEvent* e)
{
    if (d_ptr->mStackedContainerWidget) {
        if (d_ptr->mStackedContainerWidget->isPopupMode()) {
            // 弹出模式时，窗口发生了移动，同步调整StackedContainerWidget的位置
            if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
                lay->layoutStackedContainerWidget();
            }
        }
    }
    QMenuBar::moveEvent(e);
}

/**
 * @brief 跟踪字体改变
 * @param event
 */
void SARibbonBar::changeEvent(QEvent* e)
{
    if (nullptr == e) {
        return;
    }
    switch (e->type()) {
    case QEvent::FontChange: {
        QFont f                       = font();
        QList< QWidget* > listWidgets = findChildren< QWidget* >();
        for (QWidget* w : listWidgets) {
            w->setFont(f);
        }
        // 字体改变总体布局调整
        if (SARibbonBarLayout* lay = qobject_cast< SARibbonBarLayout* >(layout())) {
            lay->resetSize();
        }
    } break;
    case QEvent::StyleChange: {
        updateRibbonGeometry();
    } break;
    case QEvent::ParentChange: {
        //! 这种是针对先new 一个对象，再设置到MainWindow的情况，例如
        //! SARibbonBar* ribbon = new SARibbonBar();
        //! mainwinodw->setRibbonBar(ribbon);
        //!
        //! 这种方式，构造的时候由于没有设置父窗口，因此，如果在构造函数绑定信号槽就有可能绑定不上（parent为空）
        //! 所以在事件里绑定
        d_ptr->initNewParent(parentWidget());
    } break;
    default:
        break;
    }
    QMenuBar::changeEvent(e);
}

/**
 * @brief 绘制tabbar下的基准线，这个方法仅仅在office2013模式下需要
 * @param painter
 */
void SARibbonBar::paintTabbarBaseLine(QPainter& painter)
{
    if (!d_ptr->mTabBarBaseLineColor.isValid()) {
        return;
    }
    painter.save();
    // 在tabbar下绘制一条线
    const int lineY = d_ptr->mRibbonTabBar->geometry().bottom();
    QPen pen(d_ptr->mTabBarBaseLineColor);
    QMargins border = contentsMargins();

    pen.setWidth(1);
    pen.setStyle(Qt::SolidLine);
    painter.setPen(pen);
    painter.drawLine(QPoint(border.left(), lineY), QPoint(width() - border.right() - 1, lineY));
    painter.restore();
}

///
/// \brief 绘制标题栏
/// \param painter
/// \param title 标题
/// \param contextCategoryRegion 当前显示的上下文标签的范围，上下文标签是可以遮挡标题栏的，因此需要知道上下文标签的范围
/// x表示左边界，y表示右边界
///
void SARibbonBar::paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion)
{
    // 如果标题不显示直接跳出
    if (!isTitleVisible()) {
        return;
    }
    painter.save();
    // 先绘制背景
    if (d_ptr->mTitleBackgroundBrush != Qt::NoBrush) {
        painter.setPen(Qt::NoPen);
        painter.setBrush(d_ptr->mTitleBackgroundBrush);
        painter.drawRect(titleRegion);
    }
    // 再绘制文本
    if (d_ptr->mTitleTextColor.isValid()) {
        painter.setPen(d_ptr->mTitleTextColor);
    } else {
        painter.setPen(palette().color(QPalette::WindowText));
    }

    painter.drawText(titleRegion, d_ptr->mTitleAligment, title);
    painter.restore();
}

/*** End of inlined file: SARibbonBar.cpp ***/

/*** Start of inlined file: SARibbonBarLayout.cpp ***/
#include <QStyle>
#include <QApplication>
#include <QScreen>

#ifndef SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
#define SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT 0
#endif
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
#include <QDebug>
#else
#endif

class SARibbonBarLayout::PrivateData
{
public:
    SARibbonBar* ribbonBar;
    QList< QLayoutItem* > items;
    QRect titleRect;
    int titleBarHeight { 30 };    ///< 标题栏高度
    int tabBarHeight { 28 };      ///< tabbar高度
    int panelTitleHeight { 15 };  ///< panel的标题栏默认高度
    int categoryHeight { 60 };    ///< Category的高度

    int maxMinWidth { 1000 };  ///< 最大的最小宽度，这个一般是屏幕宽度的0.8，避免太大导致超过屏幕
    int minWidth { 500 };
    int minHeight { 0 };
    bool isTabOnTitle { false };  ///< 是否tab在标题栏上
    std::unique_ptr< int > userDefTitleBarHeight;  ///< 用户定义的标题栏高度，正常不使用用户设定的高度，而是使用自动计算的高度
    std::unique_ptr< int > userDefTabBarHeight;  ///< 用户定义的tabbar高度，正常不使用用户设定的高度，而是使用自动计算的高度
    std::unique_ptr< int > userDefCategoryHeight;  ///< 用户定义的Category的高度，正常不使用用户设定的高度，而是使用自动计算的高度
    QSize systemButtonSize;  ///< 由SARibbonMainWindow告诉窗口的关闭最大化等按钮的尺寸
    bool isApplicationButtonVerticalExpansion {
        false
    };  ///< Application button是否纵向扩展，纵向扩展的Application button将占用title和tab的高度

public:
    PrivateData(SARibbonBar* bar) : ribbonBar(bar), systemButtonSize(0, 0)
    {
        // 获取主屏幕的尺寸
        QScreen* primaryScreen = QGuiApplication::primaryScreen();
        QRect screenGeometry   = primaryScreen->geometry();
        if (currentRibbonMode() == SARibbonBar::MinimumRibbonMode) {
            minHeight = getActualTitleBarHeight() + (isTabOnTitle ? 0 : getActualTabBarHeight());
        } else {
            minHeight = getActualTitleBarHeight() + getActualCategoryHeight()
                        + (isTabOnTitle ? 0 : getActualTabBarHeight());
        }
        maxMinWidth = screenGeometry.width() * 0.8;  // 屏幕宽度
    }

    SARibbonTabBar* ribbonTabBar() const
    {
        return ribbonBar->ribbonTabBar();
    }

    SARibbonStackedWidget* stackedContainerWidget() const
    {
        return ribbonBar->ribbonStackedWidget();
    }

    SARibbonQuickAccessBar* quickAccessBar() const
    {
        return ribbonBar->quickAccessBar();
    }

    SARibbonButtonGroupWidget* rightButtonGroup() const
    {
        return ribbonBar->rightButtonGroup();
    }

    SARibbonTitleIconWidget* titleIconWidget() const
    {
        return ribbonBar->titleIconWidget();
    }

    QAbstractButton* applicationButton() const
    {
        return ribbonBar->applicationButton();
    }

    SARibbonBar::RibbonMode currentRibbonMode() const
    {
        return ribbonBar->currentRibbonState();
    }

    QMargins contentsMargins() const
    {
        return ribbonBar->contentsMargins();
    }

    SARibbonBar::RibbonStyles ribbonStyle() const
    {
        return ribbonBar->currentRibbonStyle();
    }

    void setTitleBarHeight(int h)
    {
        if (!userDefTitleBarHeight) {
            userDefTitleBarHeight = std::make_unique< int >(h);
        } else {
            *userDefTitleBarHeight = h;
        }
    }

    void setTabBarHeight(int h)
    {
        if (!userDefTabBarHeight) {
            userDefTabBarHeight = std::make_unique< int >(h);
        } else {
            *userDefTabBarHeight = h;
        }
    }

    void setCategoryHeight(int h)
    {
        if (!userDefCategoryHeight) {
            userDefCategoryHeight = std::make_unique< int >(h);
        } else {
            *userDefCategoryHeight = h;
        }
    }

    int getActualTitleBarHeight() const
    {
        if (userDefTitleBarHeight) {
            return *userDefTitleBarHeight;
        } else {
            return titleBarHeight;
        }
    }

    int getActualTabBarHeight() const
    {
        if (userDefTabBarHeight) {
            return *userDefTabBarHeight;
        } else {
            return tabBarHeight;
        }
    }

    int getActualCategoryHeight() const
    {
        if (userDefCategoryHeight) {
            return *userDefCategoryHeight;
        } else {
            return categoryHeight;
        }
    }

    void estimateSizeHint()
    {
        titleBarHeight = calcDefaultTitleBarHeight();
        // tabBarHeight有大于0的值说明用户设置了，就使用用户设置的值
        tabBarHeight   = calcDefaultTabBarHeight();
        categoryHeight = calcCategoryHeight();
    }

    int systemTabBarHeight() const
    {
        QStyle* style = ribbonBar->style();
        return style->pixelMetric(QStyle::PM_TabBarBaseHeight) + style->pixelMetric(QStyle::PM_TabBarTabHSpace)
               + style->pixelMetric(QStyle::PM_TabBarTabOverlap);
    }

    /**
     * @brief 估算tabbar的高度
     * @param fm
     * @return
     */
    int calcDefaultTabBarHeight()
    {
        int defaultHeight = systemTabBarHeight();
        int fontHeight = ribbonBar->fontMetrics().lineSpacing();  // 不要用height，像宋体这种字体，height=12，lineSpacing=14，有些就无法显示
        int defaultHeight2 = fontHeight * 1.6;
        if (defaultHeight2 < fontHeight + 10) {
            defaultHeight2 = fontHeight + 10;  // 主要为了满足office2021主题，tab下有个4px的横杠
        }
        int r = qMax(defaultHeight, defaultHeight2);
        if (r < 20) {
            r = 20;
        }
        return r;
    }

    /**
     * @brief 估算标题栏的高度
     * @param fm
     * @return
     */
    int calcDefaultTitleBarHeight()
    {
        int defaultHeight  = ribbonBar->style()->pixelMetric(QStyle::PM_TitleBarHeight);
        int defaultHeight2 = ribbonBar->fontMetrics().height() * 1.8;
        int r              = qMax(defaultHeight, defaultHeight2);
        if (r < 25) {
            r = 25;
        }
        return r;
    }

    /**
     * @brief 估算category的高度
     * @note 经过对照，1.6行高和office的高度比较接近
     * @param fm
     * @param s
     * @return
     */
    int calcCategoryHeight()
    {
        int textH = ribbonBar->fontMetrics().lineSpacing();  // 这里用linespace，因为在换行的情况下，行距是不可忽略的，ribbon的大按钮默认是2行
        if (ribbonBar->isThreeRowStyle()) {
            // 5.5=（3*1.6+1） （三行）,1是给paneltitle预留的
            return textH * 4.8 + panelTitleHeight;
        } else {
            // 3=2*1.6
            return textH * 3.2 + panelTitleHeight;
        }
        return (textH * 4.8 + panelTitleHeight);
    }

    // 计算MainBar高度
    static int
    calcMainBarHeight(int tabHegith, int titleHeight, int categoryHeight, bool tabOnTitle, SARibbonBar::RibbonMode rMode)
    {
        if (rMode == SARibbonBar::MinimumRibbonMode) {
            // 最小模式，没有categoryHeight
            if (tabOnTitle) {
                return titleHeight;
            } else {
                return titleHeight + tabHegith;
            }
        } else {
            if (tabOnTitle) {
                return titleHeight + categoryHeight;
            } else {
                return tabHegith + titleHeight + categoryHeight;
            }
        }
        return tabHegith + titleHeight + categoryHeight;
    }

    /**
     * @brief 重新计算尺寸
     */
    void resetSize()
    {
        estimateSizeHint();
        int mainBarHeight = calcMainBarHeight(getActualTabBarHeight(),
                                              getActualTitleBarHeight(),
                                              getActualCategoryHeight(),
                                              isTabOnTitle,
                                              ribbonBar->currentRibbonState());
        // 处于最小模式下时，bar的高度为tabbar的bottom,这个调整必须在resize event之后
        ribbonBar->setFixedHeight(mainBarHeight);
        minHeight = mainBarHeight;  // minHeight和mainBarHeight一致
    }

    int minimumModeMainBarHeight()
    {
        return calcMainBarHeight(getActualTabBarHeight(),
                                 getActualTitleBarHeight(),
                                 getActualCategoryHeight(),
                                 isTabOnTitle,
                                 SARibbonBar::MinimumRibbonMode);
    }

    int normalModeMainBarHeight()
    {
        return calcMainBarHeight(getActualTabBarHeight(),
                                 getActualTitleBarHeight(),
                                 getActualCategoryHeight(),
                                 isTabOnTitle,
                                 SARibbonBar::NormalRibbonMode);
    }
};

SARibbonBarLayout::SARibbonBarLayout(SARibbonBar* parent) : QLayout(parent), d_ptr(new PrivateData(parent))
{
    init();
}

SARibbonBarLayout::~SARibbonBarLayout()
{
    while (!d_ptr->items.isEmpty()) {
        QLayoutItem* item = d_ptr->items.takeFirst();
        if (item->widget()) {
            item->widget()->hide();
        }
        delete item;
    }
}

void SARibbonBarLayout::init()
{
    // 不需要初始化子控件，它们会从ribbonBar获取
}

void SARibbonBarLayout::addItem(QLayoutItem* item)
{
    d_ptr->items.append(item);
}

QLayoutItem* SARibbonBarLayout::itemAt(int index) const
{
    if (index >= 0 && index < d_ptr->items.size()) {
        return d_ptr->items.at(index);
    }
    return nullptr;
}

QLayoutItem* SARibbonBarLayout::takeAt(int index)
{
    if (index < 0 || index >= d_ptr->items.count()) {
        return nullptr;
    }
    return d_ptr->items.takeAt(index);
}

int SARibbonBarLayout::count() const
{
    return d_ptr->items.size();
}

QSize SARibbonBarLayout::sizeHint() const
{
    int height = d_ptr->minHeight;
    int width  = d_ptr->minWidth;
    if (width > d_ptr->maxMinWidth) {
        width = d_ptr->maxMinWidth;
    }
    return QSize(width, height);
}

QSize SARibbonBarLayout::minimumSize() const
{
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "SARibbonBarLayout::minimumSize() = " << sizeHint();
#endif
    return sizeHint();
}

void SARibbonBarLayout::setGeometry(const QRect& rect)
{
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "setGeometry=" << rect;
#endif
    QLayout::setGeometry(rect);
    doLayout();
}

void SARibbonBarLayout::doLayout()
{
    if (isLooseStyle()) {
        resizeInLooseStyle();
    } else {
        resizeInCompactStyle();
    }
}

bool SARibbonBarLayout::isLooseStyle() const
{
    return SARibbonBar::isLooseStyle(d_ptr->ribbonStyle());
}

bool SARibbonBarLayout::isCompactStyle() const
{
    return SARibbonBar::isCompactStyle(d_ptr->ribbonStyle());
}

const QRect& SARibbonBarLayout::titleRect() const
{
    return d_ptr->titleRect;
}

/**
 * @brief 计算tabbar的最小尺寸
 *
 * 此函数的作用是在tabbar居中布局时，tabbar不是占满整个宽度，而是按照最小尺寸来占据宽度，让tabbar的长度刚刚好，
 * 这样能让出mainwindow的空间，接受鼠标事件，从而实现拖动等操作，否则tabbar占用整个顶栏，鼠标无法点击到mainwindow
 * @return
 */
int SARibbonBarLayout::calcMinTabBarWidth() const
{
    if (!d_ptr->ribbonTabBar()) {
        return 0;
    }
    const QMargins& mg = d_ptr->ribbonTabBar()->tabMargin();
    return d_ptr->ribbonTabBar()->sizeHint().width() + (mg.left() + mg.right());
}

void SARibbonBarLayout::setSystemButtonSize(const QSize& size)
{
    d_ptr->systemButtonSize = size;
}

void SARibbonBarLayout::setTabOnTitle(bool on)
{
    if (d_ptr->isTabOnTitle != on) {
        d_ptr->isTabOnTitle = on;
        d_ptr->resetSize();
    }
}

/**
 * @brief 设置tab在title上面，这样可以省略title区域
 * @param on
 */
bool SARibbonBarLayout::isTabOnTitle() const
{
    return d_ptr->isTabOnTitle;
}

/**
   @brief 最小模式下的高度
   @return
 */
int SARibbonBarLayout::minimumModeMainBarHeight() const
{
    return d_ptr->minimumModeMainBarHeight();
}

/**
 * @brief 正常模式下的高度
 *
 * 有可能SARibbonBar::height和mainBarHeight不相等，这种情况发生在RibbonState::MinimumRibbonMode状态下
 * @return 高度
 */
int SARibbonBarLayout::normalModeMainBarHeight() const
{
    return d_ptr->normalModeMainBarHeight();
}

/**
   @brief tabBar的高度
   @return
 */
int SARibbonBarLayout::tabBarHeight() const
{
    return d_ptr->getActualTabBarHeight();
}

/**
 * @brief 设置tabBar的高度
 * @param h
 */
void SARibbonBarLayout::setTabBarHeight(int h)
{
    d_ptr->setTabBarHeight(h);
}

/**
   @brief 返回标题栏高度
   @sa setTitleBarHeight
   @return
 */
int SARibbonBarLayout::titleBarHeight() const
{
    return d_ptr->getActualTitleBarHeight();
}

/**
   @brief 设置标题栏的高度
   @sa titleBarHeight
   @note 此操作会发射@ref titleBarHeightChanged 信号
   @param h
 */
void SARibbonBarLayout::setTitleBarHeight(int h)
{
    d_ptr->setTitleBarHeight(h);
}

/**
 * @brief category的高度
 * @return
 */
int SARibbonBarLayout::categoryHeight() const
{
    return d_ptr->getActualCategoryHeight();
}

/**
 * @brief 设置category的高度
 * @param h
 */
void SARibbonBarLayout::setCategoryHeight(int h)
{
    d_ptr->setCategoryHeight(h);
}

/**
 * @brief SARibbonBarLayout::panelTitleHeight
 * @return
 */
int SARibbonBarLayout::panelTitleHeight() const
{
    return d_ptr->panelTitleHeight;
}

/**
 * @brief 设置panel的高度
 * @param h
 */
void SARibbonBarLayout::setPanelTitleHeight(int h)
{
    d_ptr->panelTitleHeight = h;
}

/**
 * @brief 设置主窗口左上角的图标
 * @param icon
 */
void SARibbonBarLayout::setWindowIcon(const QIcon& icon)
{
    titleIconWidget()->setIcon(icon);
}

/**
 * @brief 左上角图标
 * @return
 */
QIcon SARibbonBarLayout::windowIcon() const
{
    return QIcon();
}

/**
 * @brief 设置ApplicationButton垂直方向扩充，这样ApplicationButton能占用标题栏和tab栏两个栏的高度
 * @param on
 */
void SARibbonBarLayout::setApplicationButtonVerticalExpansion(bool on)
{
    d_ptr->isApplicationButtonVerticalExpansion = on;
    if (on) {
        if (SARibbonTitleIconWidget* iconWidget = titleIconWidget()) {
            iconWidget->setVisible(!on);
        }
    }
}

/**
 * @brief applicationButton是否是在垂直方向扩充
 *
 * 默认为false
 * @return
 */
bool SARibbonBarLayout::isApplicationButtonVerticalExpansion() const
{
    return d_ptr->isApplicationButtonVerticalExpansion;
}

/**
 * @brief tab是否在title上面
 * @return
 */
SARibbonBar* SARibbonBarLayout::ribbonBar() const
{
    return d_ptr->ribbonBar;
}

SARibbonTabBar* SARibbonBarLayout::ribbonTabBar() const
{
    return d_ptr->ribbonTabBar();
}

SARibbonStackedWidget* SARibbonBarLayout::stackedContainerWidget() const
{
    return d_ptr->stackedContainerWidget();
}

SARibbonQuickAccessBar* SARibbonBarLayout::quickAccessBar() const
{
    return d_ptr->quickAccessBar();
}

SARibbonButtonGroupWidget* SARibbonBarLayout::rightButtonGroup() const
{
    return d_ptr->rightButtonGroup();
}

QAbstractButton* SARibbonBarLayout::applicationButton() const
{
    return d_ptr->applicationButton();
}

SARibbonTitleIconWidget* SARibbonBarLayout::titleIconWidget() const
{
    return d_ptr->titleIconWidget();
}

void SARibbonBarLayout::layoutTitleRect()
{
    SARibbonBar* ribbon                    = d_ptr->ribbonBar;
    SARibbonQuickAccessBar* quickAccessBar = d_ptr->quickAccessBar();
    SARibbonTabBar* ribbonTabBar           = d_ptr->ribbonTabBar();
    const QMargins border                  = d_ptr->contentsMargins();
    const int validTitleBarHeight          = d_ptr->getActualTitleBarHeight();

    // 计算标题栏区域
    if (isCompactStyle()) {
        // 紧凑模式,紧凑模式的标题栏在tabbar的剩余空间中
        int titleStart = ribbonTabBar->geometry().right();
        int titleWidth = quickAccessBar ? (quickAccessBar->x() - titleStart)
                                        : (ribbon->width() - titleStart - d_ptr->systemButtonSize.width());
        if (titleWidth > 10) {
            d_ptr->titleRect = QRect(titleStart, border.top(), titleWidth, validTitleBarHeight);
        } else {
            // 标题栏过小，就不显示
            d_ptr->titleRect = QRect();
        }
    } else {
        const int tabX = ribbonTabBar->geometry().x();
        // 三行宽松模式
        int contextRegionLeft  = ribbon->width();
        int contextRegionRight = -1;

        // 使用上下文标签的视觉数据
        // 上下文标签会占用宽松模式下的标题栏位置，因此，要计算此时标题栏应该在哪里显示
        QList< int > visibleContextIndex = ribbon->currentVisibleContextCategoryTabIndexs();
        if (!visibleContextIndex.empty()) {
            int edgeVal = ribbonTabBar->tabRect(visibleContextIndex.first()).left() + tabX;
            if (edgeVal < contextRegionLeft) {
                contextRegionLeft = edgeVal;
            }
            edgeVal = d_ptr->ribbonTabBar()->tabRect(visibleContextIndex.last()).right() + tabX;
            if (edgeVal > contextRegionRight) {
                contextRegionRight = edgeVal;
            }
        }
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
        qDebug() << "  contextRegionLeft=" << contextRegionLeft << ",contextRegionRight=" << contextRegionRight;
#endif
        int x1 = border.left();
        if (quickAccessBar) {
            x1 = quickAccessBar->geometry().right() + 1;
        }
        int x2 = ribbon->width() - d_ptr->systemButtonSize.width() - border.right();

        if (contextRegionRight < 0) {
            // 说明没有上下文标签，那么标题直接放在quickAccessBar到systembar之间
            d_ptr->titleRect = QRect(QPoint(x1, border.top()), QPoint(x2, validTitleBarHeight + border.top()));
        } else {
            int leftwidth  = contextRegionLeft - x1;
            int rightwidth = x2 - contextRegionRight;
            if (rightwidth > leftwidth) {
                d_ptr->titleRect =
                    QRect(QPoint(contextRegionRight, border.top()), QPoint(x2, validTitleBarHeight + border.top()));
            } else {
                d_ptr->titleRect =
                    QRect(QPoint(x1, border.top()), QPoint(contextRegionLeft, validTitleBarHeight + border.top()));
            }
        }
    }
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "updateTitleRect=" << d_ptr->titleRect;
#endif
}

void SARibbonBarLayout::resetSize()
{
    d_ptr->resetSize();
}

void SARibbonBarLayout::layoutStackedContainerWidget()
{
    SARibbonBar* ribbon                  = ribbonBar();
    SARibbonStackedWidget* stackedWidget = stackedContainerWidget();
    SARibbonTabBar* tabBar               = ribbonTabBar();
    if (!ribbon || !stackedWidget || !tabBar) {
        return;
    }
    QMargins border            = d_ptr->contentsMargins();
    QRect ribbonTabBarGeometry = tabBar->geometry();

    int x = border.left();
    int y = ribbonTabBarGeometry.bottom() + 1;
    int w = ribbon->width() - border.left() - border.right();
    int h = d_ptr->getActualCategoryHeight();

    if (stackedWidget->isPopupMode()) {
        QPoint absPosition = ribbon->mapToGlobal(QPoint(x, y));
        x                  = absPosition.x();
        y                  = absPosition.y();
    }
    // 受布局影响，这里不能使用stackedWidget->setGeometry(x, y, w, h);
    // stackedWidget->setGeometry(x, y, w, h);
    //    stackedWidget->move(x, y);
    //    stackedWidget->setFixedSize(QSize(w, h));
    stackedWidget->setNormalGeometry(QRect(x, y, w, h));
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "resizeStackedContainerWidget,stackedWidget Geometry:" << stackedWidget->geometry()
             << "request set w=" << w << ",h=" << h;
#endif
}
/**
 * @brief 让category重新布局
 *
 *这个函数在调整category的对其方式的时候调用，由于对其方式改变StackedContainerWidget的尺寸没有改变，但category要重新布局,因此需要发射一个
 */
void SARibbonBarLayout::layoutCategory()
{
    if (SARibbonStackedWidget* stackedWidget = stackedContainerWidget()) {
        stackedWidget->layoutRequestInnerWidgets();
    }
}

void SARibbonBarLayout::resizeInLooseStyle()
{

    QMargins border = d_ptr->contentsMargins();
    int x           = border.left();
    int y           = border.top();

    const int validTitleBarHeight = d_ptr->getActualTitleBarHeight();
    const int titleBarControlHeight = validTitleBarHeight - 2;  // 标题栏上的控件高度是标题栏高度-2，上下各减1px
    const int tabH                = d_ptr->getActualTabBarHeight();
    const int tabBarControlHeight = tabH;  // tabbar上面的控件高度是tabbar高度-2，上下各减1px
    const bool isAppBtnVExpan     = d_ptr->isApplicationButtonVerticalExpansion;
    int barMinWidth               = 0;  ///< 记录ribbonBar的最小宽度，这个用于给推荐宽度
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "resizeInLooseStyle,validTitleBarHeight=" << validTitleBarHeight << ",tabH=" << tabH;
#endif
    SARibbonBar* ribbon = ribbonBar();
    /// 1. 布局corner widget
    if (QWidget* connerL = ribbon->cornerWidget(Qt::TopLeftCorner)) {
        if (connerL->isVisibleTo(ribbon)) {
            QSize connerSize = connerL->sizeHint();
            connerSize       = SA::scaleSizeByHeight(connerSize, validTitleBarHeight);
            connerL->setGeometry(x, y, connerSize.width(), connerSize.height());
            x = connerL->geometry().right() + 5;
        }
    }
    /// 2. 布局图标窗口或app button
    if (isAppBtnVExpan) {
        //! 如果Application button是纵向扩展模式，那么不显示titleicon
        //! 纵向扩展的Application button将占用title和tab的高度
        if (auto appBtn = applicationButton()) {
            if (appBtn->isVisibleTo(ribbon)) {
                QSize appBtnSize = appBtn->sizeHint();
                // 纵向扩展模式，appBtn的高度撑满titleBarControlHeight+tabH
                int appHeight = titleBarControlHeight + tabH;
                appHeight -= 2;  // 上下留1px
                appBtnSize = SA::scaleSizeByHeight(appBtnSize, appHeight, 1.5);
                appBtn->setGeometry(x, y + 1, appBtnSize.width(), appBtnSize.height());
                x = appBtn->geometry().right();
            }
        }
    } else {
        //! Application button不是纵向扩展，显示icon
        if (SARibbonTitleIconWidget* titleicon = titleIconWidget()) {
            if (titleicon->isVisibleTo(ribbon)) {
                QSize titleiconSizeHint = titleicon->sizeHint();
                titleiconSizeHint.scale(titleBarControlHeight, titleBarControlHeight, Qt::KeepAspectRatio);
                titleicon->setGeometry(x, y + 1, titleiconSizeHint.width(), titleiconSizeHint.height());
                x += titleiconSizeHint.width();
            }
        }
    }
    /// 2. 布局quick access bar
    if (auto qb = quickAccessBar()) {
        if (qb->isVisibleTo(ribbon)) {
            QSize quickAccessBarSize = qb->sizeHint();
            //! 这里不用SA::scaleSizeByHeight缩减quickAccessBar的比例：
            //! quickAccessBarSize = SA::scaleSizeByHeight(quickAccessBarSize, titleBarControlHeight);
            //! 原因是，如果quickAccessBar最后是一个widget，如果长度不足，这个widget是不会显示出来，假如默认QToolBar的高度是32，
            //! 而titlebar的高度一是28，肯定会导致宽度缩减，如果宽度缩减，且最后是一个窗口，那么这个窗口会不显示
            //! 针对工具栏类型的窗口，宽度高度都不进行比例缩减
            // 上下留1px的边线
            qb->setGeometry(x, y + 1, quickAccessBarSize.width(), titleBarControlHeight);
        }
    }

    /// titleBar上的元素布局完成，开始布局第二行
    x = border.left();
    y += validTitleBarHeight;  // 此时，y值在titlebar下面

    /// 3. 布局 applicationButton
    if (auto appBtn = applicationButton()) {
        if (appBtn->isVisibleTo(ribbon)) {
            if (isAppBtnVExpan) {
                ///! 如果是纵向扩展的Application button则只更新x位置
                x = appBtn->geometry().right();
            } else {
                QSize appBtnSize = appBtn->sizeHint();
                // appBtnSize的sizehint是根据文字宽度来推荐，如果按高度来扩展，会显得有点大，直接设置高度，又显得有点小
                // 因此宽高不按1:1比例扩展，按1:1.5比例扩展，也就是高度扩展3倍，宽度扩展3/1.5倍
                // 目前看这个比例相对比较协调
                appBtnSize = SA::scaleSizeByHeight(appBtnSize, tabBarControlHeight, 1.5);
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
                qDebug() << "scaleSizeByHeight tabBarControlHeight" << tabBarControlHeight << ",appBtnSize=" << appBtnSize;
#endif
                appBtn->setGeometry(x, y + 1, appBtnSize.width(), appBtnSize.height());
                x = appBtn->geometry().right();
                // 累加到最小宽度中
                barMinWidth += appBtnSize.width();
            }
        }
    }

    /// 4. 布局TopRightCorner窗口
    /// cornerWidget(Qt::TopRightCorner)是一定要配置的，对于多文档窗口，子窗口的缩放恢复按钮就是通过这个窗口实现，
    /// 由于这个窗口一定要在最右，因此先对这个窗口进行布局
    int endX = ribbonBar()->width() - border.right();

    if (QWidget* connerTR = ribbon->cornerWidget(Qt::TopRightCorner)) {
        if (connerTR->isVisibleTo(ribbon)) {
            QSize connerSize = connerTR->sizeHint();
            connerSize       = SA::scaleSizeByHeight(connerSize, tabBarControlHeight);
            endX -= connerSize.width();
            connerTR->setGeometry(endX, y + 1, connerSize.width(), connerSize.height());
            // 累加到最小宽度中
            barMinWidth += connerSize.width();
        }
    }

    /// 5. tabBar右边的附加按钮组rightButtonGroup，这里一般会附加一些类似登录等按钮组
    if (auto rightBtnGroup = rightButtonGroup()) {
        if (rightBtnGroup->isVisibleTo(ribbon)) {
            QSize rightBtnGroupSize = rightBtnGroup->sizeHint();
            //! 这里不用SA::scaleSizeByHeight缩减rightButtonGroup的比例：
            //! rightBtnGroupSize       = SA::scaleSizeByHeight(rightBtnGroupSize, tabBarControlHeight);
            //! 针对工具栏类型的窗口，宽度高度都不进行比例缩减
            endX -= 1;  // 先偏移1px
            endX -= rightBtnGroupSize.width();
            d_ptr->rightButtonGroup()->setGeometry(endX, y + 1, rightBtnGroupSize.width(), tabBarControlHeight);
            // 累加到最小宽度中
            barMinWidth += rightBtnGroupSize.width();
        }
    }

    /// 6.布局tabbar，此时已经确定了applicationButton的位置，以及最右边rightButtonGroup的位置，剩下的区域都给tabbar

    int tabBarWidth = endX - x;
    // 这里极度压缩有可能导致负数因此不能小于0
    if (tabBarWidth < 10) {
        tabBarWidth = 10;
    }
    int mintabBarWidth = calcMinTabBarWidth();
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "mintabBarWidth=" << mintabBarWidth;
#endif
    // 累加到最小宽度中
    barMinWidth += mintabBarWidth;
    if (auto tabbar = ribbonTabBar()) {
        if (ribbon->ribbonAlignment() == SARibbonAlignment::AlignLeft) {
            // 左对齐的tabbar，直接设置位置
            tabbar->setGeometry(x, y, tabBarWidth, tabH);
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
            qDebug() << " tabbar->setGeometry x=" << x << ",y=" << y << ",tabBarWidth=" << tabBarWidth << ",tabH=" << tabH;
#endif
        } else {
            // 居中对齐的情况下，Tab要居中显示
            // 得到tab的推荐尺寸
            if (mintabBarWidth >= tabBarWidth) {
                // 这时tabbar没有居中对齐的必要性，空间位置不够了
                tabbar->setGeometry(x, y, tabBarWidth, tabH);
            } else {
                // 说明tabbar的宽度有居中的可能性
                int xoffset = (tabBarWidth - mintabBarWidth) / 2;
                tabbar->setGeometry(x + xoffset, y, mintabBarWidth, tabH);
            }
        }
    }

    d_ptr->minWidth = barMinWidth;
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "minWidth=" << barMinWidth;
#endif
    // 5. 更新标题区域
    layoutTitleRect();

    // 6. 调整 stackedContainerWidget
    layoutStackedContainerWidget();
}

void SARibbonBarLayout::resizeInCompactStyle()
{
    QMargins border               = d_ptr->contentsMargins();
    int x                         = border.left();
    int y                         = border.top();
    SARibbonBar* ribbon           = ribbonBar();
    int barMinWidth               = 0;  ///< 记录ribbonBar的最小宽度，这个用于给推荐宽度
    const int validTitleBarHeight = d_ptr->getActualTitleBarHeight();
    const int titleBarControlHeight = validTitleBarHeight - 2;  // 标题栏上的控件高度是标题栏高度-2，上下各减1px
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "resizeInCompactStyle,validTitleBarHeight=" << validTitleBarHeight;
#endif
    /// 1.  布局corner widget - TopLeftCorner
    if (QWidget* connerL = ribbon->cornerWidget(Qt::TopRightCorner)) {
        if (connerL->isVisibleTo(ribbon)) {
            QSize connerSize = connerL->sizeHint();
            connerSize       = SA::scaleSizeByHeight(connerSize, validTitleBarHeight);
            connerL->setGeometry(x, y, connerSize.width(), connerSize.height());
            x = connerL->geometry().right();
            // 累加到最小宽度中
            barMinWidth += connerSize.width();
        }
    }
    /// 2. 布局图标窗口
    if (SARibbonTitleIconWidget* titleicon = titleIconWidget()) {
        if (titleicon->isVisibleTo(ribbon)) {
            QSize titleiconSizeHint = titleicon->sizeHint();
            titleiconSizeHint.scale(titleBarControlHeight, titleBarControlHeight, Qt::KeepAspectRatio);
            titleicon->setGeometry(x, y + 1, titleiconSizeHint.width(), titleiconSizeHint.height());
            x = titleicon->geometry().right();
            barMinWidth += titleiconSizeHint.width();
        }
    }
    /// 2.  布局applicationButton
    if (auto appBtn = applicationButton()) {
        if (appBtn->isVisibleTo(ribbon)) {
            QSize appBtnSize = appBtn->sizeHint();
            appBtnSize       = SA::scaleSizeByHeight(appBtnSize, titleBarControlHeight, 1.5);
            appBtn->setGeometry(x, y + 1, appBtnSize.width(), appBtnSize.height());
            x = appBtn->geometry().right();
            // 累加到最小宽度中
            barMinWidth += appBtnSize.width();
        }
    }

    int endX = ribbon->width() - border.right();
    if (ribbon->isUseRibbonFrame()) {
        endX -= d_ptr->systemButtonSize.width();
    }

    /// 3. 布局corner widget TopRightCorner
    if (QWidget* connerW = ribbon->cornerWidget(Qt::TopRightCorner)) {
        if (connerW->isVisibleTo(ribbon)) {
            QSize connerSize = connerW->sizeHint();
            connerSize       = SA::scaleSizeByHeight(connerSize, validTitleBarHeight);
            endX -= connerSize.width();
            connerW->setGeometry(endX, y, connerSize.width(), connerSize.height());
            // 累加到最小宽度中
            barMinWidth += connerSize.width();
        }
    }

    /// 4. 布局右边按钮组rightButtonGroup
    if (auto rightBtnGroup = rightButtonGroup()) {
        if (rightBtnGroup->isVisibleTo(ribbon)) {
            QSize rightBtnGroupSize = rightBtnGroup->sizeHint();
            // rightBtnGroupSize       = SA::scaleSizeByHeight(rightBtnGroupSize, titleBarControlHeight);
            endX -= rightBtnGroupSize.width();
            d_ptr->rightButtonGroup()->setGeometry(endX, y + 1, rightBtnGroupSize.width(), titleBarControlHeight);
            // 累加到最小宽度中
            barMinWidth += rightBtnGroupSize.width();
        }
    }

    /// 5. 布局quick access bar
    if (auto qb = quickAccessBar()) {
        if (qb->isVisibleTo(ribbon)) {
            QSize quickAccessBarSize = qb->sizeHint();
            // quickAccessBarSize       = SA::scaleSizeByHeight(quickAccessBarSize, titleBarControlHeight);
            endX -= quickAccessBarSize.width();
            qb->setGeometry(endX, y + 1, quickAccessBarSize.width(), titleBarControlHeight);
            // 累加到最小宽度中
            barMinWidth += quickAccessBarSize.width();
        }
    }

    /// 6.布局 tab bar
    int tabH = d_ptr->getActualTabBarHeight();
    if (tabH > titleBarControlHeight) {
        // 这种直接把tabH设置为validTitleBarHeight
        tabH = titleBarControlHeight;
    }
    int tabBarWidth = endX - x;
    // 这里极度压缩有可能导致负数因此不能小于0
    if (tabBarWidth < 10) {
        tabBarWidth = 10;
    }
    // 紧凑模式下，tabbar要用最小宽度布局，否则会遮挡标题栏的点击，导致无法移动
    int mintabBarWidth = calcMinTabBarWidth();
    // 累加到最小宽度中
    barMinWidth += mintabBarWidth;
    if (auto tabbar = ribbonTabBar()) {
        y = border.top() + (titleBarControlHeight - tabH);
        if (ribbon->ribbonAlignment() == SARibbonAlignment::AlignLeft) {
            // 左对齐的tabbar，直接设置位置
            if (mintabBarWidth < tabBarWidth) {
                tabBarWidth = mintabBarWidth;
            }
            tabbar->setGeometry(x, y, tabBarWidth, tabH);
        } else {
            // 居中对齐的情况下，Tab要居中显示
            // 得到tab的推荐尺寸
            int mintabBarWidth = calcMinTabBarWidth();
            if (mintabBarWidth >= tabBarWidth) {
                // 这时tabbar没有居中对齐的必要性，空间位置不够了
                tabbar->setGeometry(x, y, tabBarWidth, tabH);
            } else {
                // 说明tabbar的宽度有居中的可能性
                int xoffset = (tabBarWidth - mintabBarWidth) / 2;
                tabbar->setGeometry(x + xoffset, y, mintabBarWidth, tabH);
            }
        }
    }

    d_ptr->minWidth = barMinWidth;
#if SARIBBONBARLAYOUT_ENABLE_DEBUG_PRINT
    qDebug() << "minWidth=" << barMinWidth;
#endif
    // 5. 更新标题区域
    layoutTitleRect();
    // 6. 调整 stackedContainerWidget
    layoutStackedContainerWidget();
}

/*** End of inlined file: SARibbonBarLayout.cpp ***/

/*** Start of inlined file: SARibbonElementFactory.cpp ***/
#include <QApplication>
#include <QFontMetrics>

SARibbonElementFactory::SARibbonElementFactory()
{
}

SARibbonElementFactory::~SARibbonElementFactory()
{
}

SARibbonBar* SARibbonElementFactory::createRibbonBar(QWidget* parent)
{
    return (new SARibbonBar(parent));
}

SARibbonTabBar* SARibbonElementFactory::createRibbonTabBar(QWidget* parent)
{
    return (new SARibbonTabBar(parent));
}

SARibbonApplicationButton* SARibbonElementFactory::createRibbonApplicationButton(QWidget* parent)
{
    return (new SARibbonApplicationButton(parent));
}

SARibbonCategory* SARibbonElementFactory::createRibbonCategory(QWidget* parent)
{
    return (new SARibbonCategory(parent));
}

SARibbonContextCategory* SARibbonElementFactory::createRibbonContextCategory(QWidget* parent)
{
    return (new SARibbonContextCategory(parent));
}

SARibbonPanel* SARibbonElementFactory::createRibbonPanel(QWidget* parent)
{
    return (new SARibbonPanel(parent));
}

SARibbonSeparatorWidget* SARibbonElementFactory::createRibbonSeparatorWidget(QWidget* parent)
{
    return (new SARibbonSeparatorWidget(parent));
}

SARibbonGallery* SARibbonElementFactory::createRibbonGallery(QWidget* parent)
{
    return (new SARibbonGallery(parent));
}

SARibbonGalleryGroup* SARibbonElementFactory::createRibbonGalleryGroup(QWidget* parent)
{
    return (new SARibbonGalleryGroup(parent));
}

SARibbonToolButton* SARibbonElementFactory::createRibbonToolButton(QWidget* parent)
{
    return (new SARibbonToolButton(parent));
}

SARibbonStackedWidget* SARibbonElementFactory::createRibbonStackedWidget(SARibbonBar* parent)
{
    return (new SARibbonStackedWidget(parent));
}

SARibbonButtonGroupWidget* SARibbonElementFactory::createButtonGroupWidget(QWidget* parent)
{
    return (new SARibbonButtonGroupWidget(parent));
}

SARibbonQuickAccessBar* SARibbonElementFactory::createQuickAccessBar(QWidget* parent)
{
    return (new SARibbonQuickAccessBar(parent));
}

SARibbonSystemButtonBar* SARibbonElementFactory::createWindowButtonGroup(QWidget* parent)
{
    return (new SARibbonSystemButtonBar(parent));
}

/**
 * @brief 创建SARibbonPanelOptionButton
 * @param panel 附属的panel
 * @return
 * @sa SARibbonPanelOptionButton
 */
SARibbonPanelOptionButton* SARibbonElementFactory::createRibbonPanelOptionButton(SARibbonPanel* panel)
{
    return (new SARibbonPanelOptionButton(panel));
}

SARibbonTitleIconWidget* SARibbonElementFactory::createRibbonTitleIconWidget(QWidget* parent)
{
    return (new SARibbonTitleIconWidget(parent));
}

/*** End of inlined file: SARibbonElementFactory.cpp ***/

/*** Start of inlined file: SARibbonElementManager.cpp ***/
SARibbonElementManager::SARibbonElementManager()
{
    mFactory.reset(new SARibbonElementFactory());
}

SARibbonElementManager::~SARibbonElementManager()
{
}

SARibbonElementManager* SARibbonElementManager::instance()
{
    static SARibbonElementManager s_instance;

    return (&(s_instance));
}

SARibbonElementFactory* SARibbonElementManager::factory()
{
    return (mFactory.data());
}

void SARibbonElementManager::setupFactory(SARibbonElementFactory* fac)
{
    mFactory.reset(fac);
}

/*** End of inlined file: SARibbonElementManager.cpp ***/

/*** Start of inlined file: SARibbonCustomizeData.cpp ***/
#include <QDebug>
#include <QObject>
////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SARibbonCustomizeData
////////////////////////////////////////////////////////////////////////////////////////////////////////

SARibbonCustomizeData::SARibbonCustomizeData()
    : indexValue(-1)
    , actionRowProportionValue(SARibbonPanelItem::Large)
    , mType(UnknowActionType)
    , mActionsManagerPointer(nullptr)
{
}

SARibbonCustomizeData::SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr)
    : indexValue(-1), actionRowProportionValue(SARibbonPanelItem::Large), mType(type), mActionsManagerPointer(mgr)
{
}

/**
 * @brief 获取CustomizeData的action type
 * @return
 */
SARibbonCustomizeData::ActionType SARibbonCustomizeData::actionType() const
{
    return (mType);
}

/**
 * @brief 设置CustomizeData的action type
 * @param a
 */
void SARibbonCustomizeData::setActionType(SARibbonCustomizeData::ActionType a)
{
    mType = a;
}

/**
 * @brief 判断是否是一个正常的CustomizeData
 *
 * 实际逻辑actionType() != UnknowActionType
 * @return 有用的CustomizeData返回true
 */
bool SARibbonCustomizeData::isValid() const
{
    return (actionType() != UnknowActionType);
}

/**
 * @brief 应用SARibbonCustomizeData到SARibbonBar
 * @param m
 * @return 如果应用失败，返回false,如果actionType==UnknowActionType直接返回false
 */
bool SARibbonCustomizeData::apply(SARibbonBar* bar) const
{
    if (nullptr == bar) {
        return (false);
    }
    switch (actionType()) {
    case UnknowActionType:
        return (false);

    case AddCategoryActionType: {
        // 添加标签
        SARibbonCategory* c = bar->insertCategoryPage(keyValue, indexValue);
        if (nullptr == c) {
            return (false);
        }
        c->setObjectName(categoryObjNameValue);
        SARibbonCustomizeData::setCanCustomize(c);
        return (true);
    }

    case AddPanelActionType: {
        // 添加panel
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* p = c->insertPanel(keyValue, indexValue);
        p->setObjectName(panelObjNameValue);
        SARibbonCustomizeData::setCanCustomize(p);
        return (true);
    }

    case AddActionActionType: {
        if (nullptr == mActionsManagerPointer) {
            return (false);
        }
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* panel = c->panelByObjectName(panelObjNameValue);
        if (nullptr == panel) {
            return (false);
        }
        QAction* act = mActionsManagerPointer->action(keyValue);
        if (nullptr == act) {
            return (false);
        }
        SARibbonCustomizeData::setCanCustomize(act);
        panel->addAction(act, actionRowProportionValue);
        return (true);
    }

    case RemoveCategoryActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        bar->removeCategory(c);
        return (true);
    }

    case RemovePanelActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* panel = c->panelByObjectName(panelObjNameValue);
        if (nullptr == panel) {
            return (false);
        }
        c->removePanel(panel);
        return (true);
    }

    case RemoveActionActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* panel = c->panelByObjectName(panelObjNameValue);
        if (nullptr == panel) {
            return (false);
        }
        QAction* act = mActionsManagerPointer->action(keyValue);
        if (nullptr == act) {
            return (false);
        }
        panel->removeAction(act);
        return (true);
    }

    case ChangeCategoryOrderActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        int currentindex = bar->categoryIndex(c);
        if (-1 == currentindex) {
            return (false);
        }
        int toindex = currentindex + indexValue;
        bar->moveCategory(currentindex, toindex);
        return (true);
    }

    case ChangePanelOrderActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* panel = c->panelByObjectName(panelObjNameValue);
        if (nullptr == panel) {
            return (false);
        }
        int panelIndex = c->panelIndex(panel);
        if (-1 == panelIndex) {
            return (false);
        }
        c->movePanel(panelIndex, panelIndex + indexValue);
        return (true);
    }

    case ChangeActionOrderActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* panel = c->panelByObjectName(panelObjNameValue);
        if (nullptr == panel) {
            return (false);
        }
        QAction* act = mActionsManagerPointer->action(keyValue);
        if (nullptr == act) {
            return (false);
        }
        int actindex = panel->actionIndex(act);
        if (actindex <= -1) {
            return (false);
        }
        panel->moveAction(actindex, actindex + indexValue);
        return (true);
    }

    case RenameCategoryActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        c->setCategoryName(keyValue);
        return (true);
    }

    case RenamePanelActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        SARibbonPanel* panel = c->panelByObjectName(panelObjNameValue);
        if (nullptr == panel) {
            return (false);
        }
        panel->setPanelName(keyValue);
        return (true);
    }

    case VisibleCategoryActionType: {
        SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue);
        if (nullptr == c) {
            return (false);
        }
        if (1 == indexValue) {
            bar->showCategory(c);
        } else {
            bar->hideCategory(c);
        }
        return (true);
    }

    default:
        break;
    }
    return (false);
}

/**
 * @brief 获取actionmanager指针
 * @return
 */
SARibbonActionsManager* SARibbonCustomizeData::actionManager()
{
    return (mActionsManagerPointer);
}

/**
 * @brief 设置ActionsManager
 * @param mgr
 */
void SARibbonCustomizeData::setActionsManager(SARibbonActionsManager* mgr)
{
    mActionsManagerPointer = mgr;
}

/**
 * @brief 创建一个AddCategoryActionType的SARibbonCustomizeData
 * @param title category 的标题
 * @param index category要插入的位置
 * @param objName category的object name
 * @return 返回AddCategoryActionType的SARibbonCustomizeData
 */
SARibbonCustomizeData SARibbonCustomizeData::makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName)
{
    SARibbonCustomizeData d(AddCategoryActionType);

    d.indexValue           = index;
    d.keyValue             = title;
    d.categoryObjNameValue = objName;
    return (d);
}

/**
 * @brief 创建一个AddPanelActionType的SARibbonCustomizeData
 * @param title panel的标题
 * @param index panel的index
 * @param categoryobjName panel的category的objectname
 * @param objName panel的objname
 * @return 返回AddPanelActionType的SARibbonCustomizeData
 */
SARibbonCustomizeData SARibbonCustomizeData::makeAddPanelCustomizeData(const QString& title,
                                                                       int index,
                                                                       const QString& categoryobjName,
                                                                       const QString& objName)
{
    SARibbonCustomizeData d(AddPanelActionType);

    d.indexValue           = index;
    d.keyValue             = title;
    d.panelObjNameValue    = objName;
    d.categoryObjNameValue = categoryobjName;
    return (d);
}

/**
 * @brief 添加action
 * @param key action的索引
 * @param mgr action管理器
 * @param rp 定义action的占位情况
 * @param categoryObjName action添加到的category的objname
 * @param panelObjName action添加到的category下的panel的objname
 * @param index action添加到的panel的索引
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeAddActionCustomizeData(const QString& key,
                                                                        SARibbonActionsManager* mgr,
                                                                        SARibbonPanelItem::RowProportion rp,
                                                                        const QString& categoryObjName,
                                                                        const QString& panelObjName)
{
    SARibbonCustomizeData d(AddActionActionType, mgr);

    d.keyValue                 = key;
    d.categoryObjNameValue     = categoryObjName;
    d.panelObjNameValue        = panelObjName;
    d.actionRowProportionValue = rp;

    return (d);
}

/**
 * @brief 创建一个RenameCategoryActionType的SARibbonCustomizeData
 * @param newname 新名字
 * @param index category的索引
 * @return 返回RenameCategoryActionType的SARibbonCustomizeData
 */
SARibbonCustomizeData SARibbonCustomizeData::makeRenameCategoryCustomizeData(const QString& newname,
                                                                             const QString& categoryobjName)
{
    SARibbonCustomizeData d(RenameCategoryActionType);

    if (categoryobjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize rename category,"
                                "but get an empty category object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.keyValue             = newname;
    d.categoryObjNameValue = categoryobjName;
    return (d);
}

/**
 * @brief 创建一个RenamePanelActionType的SARibbonCustomizeData
 * @param newname panel的名字
 * @param indexValue panel的索引
 * @param categoryobjName panel对应的category的object name
 * @return 返回RenamePanelActionType的SARibbonCustomizeData
 */
SARibbonCustomizeData SARibbonCustomizeData::makeRenamePanelCustomizeData(const QString& newname,
                                                                          const QString& categoryobjName,
                                                                          const QString& panelObjName)
{
    SARibbonCustomizeData d(RenamePanelActionType);

    if (panelObjName.isEmpty() || categoryobjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize rename panel,"
                                "but get an empty category/panel object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.keyValue             = newname;
    d.panelObjNameValue    = panelObjName;
    d.categoryObjNameValue = categoryobjName;
    return (d);
}

/**
 * @brief 对应ChangeCategoryOrderActionType
 * @param categoryobjName 需要移动的categoryobjName
 * @param moveindex 移动位置，-1代表向上（向左）移动一个位置，1带表向下（向右）移动一个位置
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(const QString& categoryobjName,
                                                                                  int moveindex)
{
    SARibbonCustomizeData d(ChangeCategoryOrderActionType);

    if (categoryobjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize change category order,"
                                "but get an empty category object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    d.indexValue           = moveindex;
    return (d);
}

/**
 * @brief 对应ChangePanelOrderActionType
 * @param categoryobjName 需要移动的panel对应的categoryobjName
 * @param panelObjName 需要移动的panelObjName
 * @param moveindex 移动位置，-1代表向上（向左）移动一个位置，1带表向下（向右）移动一个位置
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeChangePanelOrderCustomizeData(const QString& categoryobjName,
                                                                               const QString& panelObjName,
                                                                               int moveindex)
{
    SARibbonCustomizeData d(ChangePanelOrderActionType);

    if (categoryobjName.isEmpty() || panelObjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize change panel order,"
                                "but get an empty category/panel object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    d.panelObjNameValue    = panelObjName;
    d.indexValue           = moveindex;
    return (d);
}

/**
 * @brief 对应ChangeActionOrderActionType
 * @param categoryobjName 需要移动的panel对应的categoryobjName
 * @param panelObjName 需要移动的panelObjName
 * @param key SARibbonActionsManager管理的key名
 * @param mgr SARibbonActionsManager指针
 * @param moveindex 移动位置，-1代表向上（向左）移动一个位置，1带表向下（向右）移动一个位置
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeChangeActionOrderCustomizeData(const QString& categoryobjName,
                                                                                const QString& panelObjName,
                                                                                const QString& key,
                                                                                SARibbonActionsManager* mgr,
                                                                                int moveindex)
{
    SARibbonCustomizeData d(ChangeActionOrderActionType, mgr);

    if (categoryobjName.isEmpty() || panelObjName.isEmpty() || key.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize change action order,"
                                "but get an empty category/panel/action object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    d.panelObjNameValue    = panelObjName;
    d.keyValue             = key;
    d.indexValue           = moveindex;
    return (d);
}

/**
 * @brief 对应RemoveCategoryActionType
 * @param categoryobjName 需要移除的objname
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeRemoveCategoryCustomizeData(const QString& categoryobjName)
{
    SARibbonCustomizeData d(RemoveCategoryActionType);

    if (categoryobjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize remove category,"
                                "but get an empty category object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    return (d);
}

/**
 * @brief 对应RemovePanelActionType
 * @param categoryobjName panel对应的category的obj name
 * @param panelObjName panel对应的 obj name
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeRemovePanelCustomizeData(const QString& categoryobjName,
                                                                          const QString& panelObjName)
{
    SARibbonCustomizeData d(RemovePanelActionType);

    if (categoryobjName.isEmpty() || panelObjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize remove panel,"
                                "but get an empty category/panel object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    d.panelObjNameValue    = panelObjName;
    return (d);
}

/**
 * @brief 对应RemoveActionActionType
 * @param categoryobjName panel对应的category的obj name
 * @param panelObjName panel对应的 obj name
 * @param key SARibbonActionsManager管理的key名
 * @param mgr SARibbonActionsManager指针
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeRemoveActionCustomizeData(const QString& categoryobjName,
                                                                           const QString& panelObjName,
                                                                           const QString& key,
                                                                           SARibbonActionsManager* mgr)
{
    SARibbonCustomizeData d(RemoveActionActionType, mgr);

    if (categoryobjName.isEmpty() || panelObjName.isEmpty() || key.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize remove action,"
                                "but get an empty category/panel/action object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    d.panelObjNameValue    = panelObjName;
    d.keyValue             = key;
    return (d);
}

/**
 * @brief SARibbonCustomizeData::makeVisibleCategoryCustomizeData
 * @param categoryobjName
 * @param isShow
 * @return
 */
SARibbonCustomizeData SARibbonCustomizeData::makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow)
{
    SARibbonCustomizeData d(VisibleCategoryActionType);

    if (categoryobjName.isEmpty()) {
        qDebug() << QObject::tr("SARibbon Warning !!! customize visible category,"
                                "but get an empty category object name,"
                                "if you want to customize SARibbon,"
                                "please make sure every element has been set object name.");
    }
    d.categoryObjNameValue = categoryobjName;
    d.indexValue           = isShow ? 1 : 0;
    return (d);
}

/**
 * @brief 判断外置属性，是否允许自定义
 * @param obj
 * @return
 */
bool SARibbonCustomizeData::isCanCustomize(QObject* obj)
{
    QVariant v = obj->property(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE);

    if (v.isValid()) {
        return (v.toBool());
    }
    return (false);
}

/**
 * @brief 设置外置属性允许自定义
 * @param obj
 * @param canbe
 */
void SARibbonCustomizeData::setCanCustomize(QObject* obj, bool canbe)
{
    obj->setProperty(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE, canbe);
}

QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex);

QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex)
{
    QList< SARibbonCustomizeData > res;

    for (int i = 0; i < csd.size(); ++i) {
        if (!willremoveIndex.contains(i)) {
            res << csd[ i ];
        }
    }
    return (res);
}

/**
 * @brief 对QList<SARibbonCustomizeData>进行简化操作
 *
 * 此函数会执行如下操作：
 * 1、针对同一个category/panel连续出现的添加和删除操作进行移除（前一步添加，后一步删除）
 *
 * 2、针对VisibleCategoryActionType，对于连续出现的操作只保留最后一步
 *
 * 3、针对RenameCategoryActionType和RenamePanelActionType操作，只保留最后一个
 *
 * 4、针对连续的ChangeCategoryOrderActionType，ChangePanelOrderActionType，ChangeActionOrderActionType进行合并为一个动作，
 * 如果合并后原地不动，则删除
 *
 * @param csd
 * @return 返回简化的QList<SARibbonCustomizeData>
 */
QList< SARibbonCustomizeData > SARibbonCustomizeData::simplify(const QList< SARibbonCustomizeData >& csd)
{
    int size = csd.size();

    if (size <= 1) {
        return (csd);
    }
    QList< SARibbonCustomizeData > res;
    QList< int > willremoveIndex;  // 记录要删除的index

    //! 首先针对连续出现的添加和删除操作进行优化
    for (int i = 1; i < size; ++i) {
        if ((csd[ i - 1 ].actionType() == AddCategoryActionType) && (csd[ i ].actionType() == RemoveCategoryActionType)) {
            if (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue) {
                willremoveIndex << i - 1 << i;
            }
        } else if ((csd[ i - 1 ].actionType() == AddPanelActionType) && (csd[ i ].actionType() == RemovePanelActionType)) {
            if ((csd[ i - 1 ].panelObjNameValue == csd[ i ].panelObjNameValue)
                && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) {
                willremoveIndex << i - 1 << i;
            }
        } else if ((csd[ i - 1 ].actionType() == AddActionActionType) && (csd[ i ].actionType() == RemoveActionActionType)) {
            if ((csd[ i - 1 ].keyValue == csd[ i ].keyValue) && (csd[ i - 1 ].panelObjNameValue == csd[ i ].panelObjNameValue)
                && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) {
                willremoveIndex << i - 1 << i;
            }
        }
    }
    res = remove_indexs(csd, willremoveIndex);
    willremoveIndex.clear();

    //! 筛选VisibleCategoryActionType，对于连续出现的操作只保留最后一步
    size = res.size();
    for (int i = 1; i < size; ++i) {
        if ((res[ i - 1 ].actionType() == VisibleCategoryActionType)
            && (res[ i ].actionType() == VisibleCategoryActionType)) {
            if (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue) {
                // 要保证操作的是同一个内容
                willremoveIndex << i - 1;  // 删除前一个只保留最后一个
            }
        }
    }
    res = remove_indexs(res, willremoveIndex);
    willremoveIndex.clear();

    //! 针对RenameCategoryActionType和RenamePanelActionType操作，只需保留最后一个
    size = res.size();
    for (int i = 0; i < size; ++i) {
        if (res[ i ].actionType() == RenameCategoryActionType) {
            // 向后查询，如果查询到有同一个Category改名，把这个索引加入删除队列
            for (int j = i + 1; j < size; ++j) {
                if ((res[ j ].actionType() == RenameCategoryActionType)
                    && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) {
                    willremoveIndex << i;
                }
            }
        } else if (res[ i ].actionType() == RenamePanelActionType) {
            // 向后查询，如果查询到有同一个panel改名，把这个索引加入删除队列
            for (int j = i + 1; j < size; ++j) {
                if ((res[ j ].actionType() == RenamePanelActionType)
                    && (res[ i ].panelObjNameValue == res[ j ].panelObjNameValue)
                    && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) {
                    willremoveIndex << i;
                }
            }
        }
    }
    res = remove_indexs(res, willremoveIndex);
    willremoveIndex.clear();

    //! 针对连续的ChangeCategoryOrderActionType，ChangePanelOrderActionType，ChangeActionOrderActionType进行合并
    size = res.size();
    for (int i = 1; i < size; ++i) {
        if ((res[ i - 1 ].actionType() == ChangeCategoryOrderActionType)
            && (res[ i ].actionType() == ChangeCategoryOrderActionType)
            && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) {
            // 说明连续两个顺序调整，把前一个indexvalue和后一个indexvalue相加，前一个删除
            res[ i ].indexValue += res[ i - 1 ].indexValue;
            willremoveIndex << i - 1;
        } else if ((res[ i - 1 ].actionType() == ChangePanelOrderActionType)
                   && (res[ i ].actionType() == ChangePanelOrderActionType)
                   && (res[ i - 1 ].panelObjNameValue == res[ i ].panelObjNameValue)
                   && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) {
            // 说明连续两个顺序调整，把前一个indexvalue和后一个indexvalue相加，前一个删除
            res[ i ].indexValue += res[ i - 1 ].indexValue;
            willremoveIndex << i - 1;
        } else if ((res[ i - 1 ].actionType() == ChangeActionOrderActionType)
                   && (res[ i ].actionType() == ChangeActionOrderActionType) && (res[ i - 1 ].keyValue == res[ i ].keyValue)
                   && (res[ i - 1 ].panelObjNameValue == res[ i ].panelObjNameValue)
                   && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) {
            // 说明连续两个顺序调整，把前一个indexvalue和后一个indexvalue相加，前一个删除
            res[ i ].indexValue += res[ i - 1 ].indexValue;
            willremoveIndex << i - 1;
        }
    }
    res = remove_indexs(res, willremoveIndex);
    willremoveIndex.clear();

    //! 上一步操作可能会产生indexvalue为0的情况，此操作把indexvalue为0的删除
    size = res.size();
    for (int i = 0; i < size; ++i) {
        if ((res[ i ].actionType() == ChangeCategoryOrderActionType) || (res[ i ].actionType() == ChangePanelOrderActionType)
            || (res[ i ].actionType() == ChangeActionOrderActionType)) {
            if (0 == res[ i ].indexValue) {
                willremoveIndex << i;
            }
        }
    }
    res = remove_indexs(res, willremoveIndex);
    willremoveIndex.clear();
    return (res);
}

/*** End of inlined file: SARibbonCustomizeData.cpp ***/

/*** Start of inlined file: SARibbonCustomizeWidget.cpp ***/
#include <QDebug>
#include <QtCore/QVariant>
#include <QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QRadioButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QTreeView>
#include <QtWidgets/QListView>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

#include <QStandardItemModel>
#include <QButtonGroup>
#include <QInputDialog>
#include <QLineEdit>
#include <QDateTime>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>

#include <QFile>
#include <QMessageBox>

////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SARibbonCustomizeWidget
////////////////////////////////////////////////////////////////////////////////////////////////////////

bool sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds)
{
    if (cds.size() <= 0) {
        return (false);
    }

    xml->writeStartElement("sa-ribbon-customize");
    for (const SARibbonCustomizeData& d : cds) {
        xml->writeStartElement("customize-data");
        xml->writeAttribute("type", QString::number(d.actionType()));
        xml->writeAttribute("index", QString::number(d.indexValue));
        xml->writeAttribute("key", d.keyValue);
        xml->writeAttribute("category", d.categoryObjNameValue);
        xml->writeAttribute("panel", d.panelObjNameValue);
        xml->writeAttribute("row-prop", QString::number(d.actionRowProportionValue));

        xml->writeEndElement();
    }
    xml->writeEndElement();
    if (xml->hasError()) {
        qWarning() << "write has error";
    }
    return (true);
}

QList< SARibbonCustomizeData > sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr)
{
    // 先找到"sa-ribbon-customize"
    while (!xml->atEnd()) {

        if (xml->isStartElement() && (xml->name().toString() == "sa-ribbon-customize")) {
            break;
        }
        xml->readNext();
    }
    QList< SARibbonCustomizeData > res;

    // 开始遍历"customize-data"
    while (!xml->atEnd()) {
        if (xml->isStartElement() && (xml->name().toString() == "customize-data")) {
            // 首先读取属性type
            SARibbonCustomizeData d;
            QXmlStreamAttributes attrs = xml->attributes();
            if (!attrs.hasAttribute("type")) {
                // 说明异常，跳过这个
                xml->readNextStartElement();
                continue;
            }
            bool isOk = false;
            int v     = xml->attributes().value("type").toInt(&isOk);
            if (!isOk) {
                // 说明异常，跳过这个
                xml->readNextStartElement();
                continue;
            }
            d.setActionType(static_cast< SARibbonCustomizeData::ActionType >(v));
            // 开始读取子对象
            if (attrs.hasAttribute("index")) {
                v = xml->attributes().value("index").toInt(&isOk);
                if (isOk) {
                    d.indexValue = v;
                }
            }
            if (attrs.hasAttribute("key")) {
                d.keyValue = attrs.value("key").toString();
            }
            if (attrs.hasAttribute("category")) {
                d.categoryObjNameValue = attrs.value("category").toString();
            }
            if (attrs.hasAttribute("panel")) {
                d.panelObjNameValue = attrs.value("panel").toString();
            }
            if (attrs.hasAttribute("row-prop")) {
                v = xml->attributes().value("row-prop").toInt(&isOk);
                if (isOk) {
                    d.actionRowProportionValue = static_cast< SARibbonPanelItem::RowProportion >(v);
                }
            }
            d.setActionsManager(mgr);
            res.append(d);
        }
        xml->readNext();
    }
    if (xml->hasError()) {
        qWarning() << xml->errorString();
    }
    return (res);
}

int sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar)
{
    int c = 0;

    for (const SARibbonCustomizeData& d : cds) {
        if (d.apply(bar)) {
            ++c;
        }
    }
    return (c);
}

int sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar)
{
    int c = 0;
    // todo 支持反向操作
    return (c);
}

bool sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr)
{
    QFile f(filePath);

    if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return (false);
    }
    f.seek(0);
    QXmlStreamReader xml(&f);

    return (SARibbonCustomizeWidget::fromXml(&xml, bar, mgr));
}

/**
 * @brief 构建SARibbonCustomizeWidget的Ui
 */
class SARibbonCustomizeWidgetUi
{
public:
    QHBoxLayout* horizontalLayoutMain;
    QVBoxLayout* verticalLayoutSelect;
    QLabel* labelSelectAction;
    QHBoxLayout* horizontalLayoutSearch;
    QComboBox* comboBoxActionIndex;
    QLineEdit* lineEditSearchAction;
    QListView* listViewSelect;
    QVBoxLayout* verticalLayoutMidButtons;
    QSpacerItem* verticalSpacerUp;
    QPushButton* pushButtonAdd;
    QPushButton* pushButtonDelete;
    QPushButton* pushButtonReset;
    QSpacerItem* verticalSpacerDown;
    QLabel* labelProportion;
    QComboBox* comboBoxActionProportion;
    QVBoxLayout* verticalLayoutResult;
    QLabel* labelCustomize;
    QHBoxLayout* horizontalLayoutCategorySelect;
    QRadioButton* radioButtonMainCategory;
    QRadioButton* radioButtonAllCategory;
    QButtonGroup* radioButtonGroup;
    QTreeView* treeViewResult;
    QHBoxLayout* horizontalLayoutActionOptBtns;
    QPushButton* pushButtonNewCategory;
    QPushButton* pushButtonNewPanel;
    QPushButton* pushButtonRename;
    QVBoxLayout* verticalLayoutRightButtons;
    QSpacerItem* verticalSpacerUp2;
    QToolButton* toolButtonUp;
    QToolButton* toolButtonDown;
    QSpacerItem* verticalSpacerDown2;

    void setupUi(QWidget* customizeWidget)
    {
        if (customizeWidget->objectName().isEmpty()) {
            customizeWidget->setObjectName(QStringLiteral("SARibbonCustomizeWidget"));
        }
        customizeWidget->resize(800, 600);
        horizontalLayoutMain = new QHBoxLayout(customizeWidget);
        horizontalLayoutMain->setObjectName(QStringLiteral("horizontalLayoutMain"));
        verticalLayoutSelect = new QVBoxLayout();
        verticalLayoutSelect->setObjectName(QStringLiteral("verticalLayoutSelect"));
        labelSelectAction = new QLabel(customizeWidget);
        labelSelectAction->setObjectName(QStringLiteral("labelSelectAction"));

        verticalLayoutSelect->addWidget(labelSelectAction);

        horizontalLayoutSearch = new QHBoxLayout();
        horizontalLayoutSearch->setObjectName(QStringLiteral("horizontalLayoutSearch"));
        comboBoxActionIndex = new QComboBox(customizeWidget);
        comboBoxActionIndex->setObjectName(QStringLiteral("comboBoxActionIndex"));
        comboBoxActionIndex->setEditable(false);

        horizontalLayoutSearch->addWidget(comboBoxActionIndex);

        lineEditSearchAction = new QLineEdit(customizeWidget);
        lineEditSearchAction->setObjectName(QStringLiteral("lineEditSearchAction"));

        horizontalLayoutSearch->addWidget(lineEditSearchAction);

        verticalLayoutSelect->addLayout(horizontalLayoutSearch);

        listViewSelect = new QListView(customizeWidget);
        listViewSelect->setObjectName(QStringLiteral("listViewSelect"));

        verticalLayoutSelect->addWidget(listViewSelect);

        horizontalLayoutMain->addLayout(verticalLayoutSelect);

        verticalLayoutMidButtons = new QVBoxLayout();
        verticalLayoutMidButtons->setObjectName(QStringLiteral("verticalLayoutMidButtons"));
        verticalSpacerUp = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayoutMidButtons->addItem(verticalSpacerUp);

        pushButtonAdd = new QPushButton(customizeWidget);
        pushButtonAdd->setObjectName(QStringLiteral("pushButtonAdd"));
        pushButtonAdd->setEnabled(false);

        verticalLayoutMidButtons->addWidget(pushButtonAdd);

        pushButtonDelete = new QPushButton(customizeWidget);
        pushButtonDelete->setObjectName(QStringLiteral("pushButtonDelete"));
        pushButtonDelete->setEnabled(false);

        verticalLayoutMidButtons->addWidget(pushButtonDelete);

        verticalSpacerDown = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayoutMidButtons->addItem(verticalSpacerDown);

        labelProportion = new QLabel(customizeWidget);
        labelProportion->setObjectName(QStringLiteral("labelProportion"));
        verticalLayoutMidButtons->addWidget(labelProportion);

        comboBoxActionProportion = new QComboBox(customizeWidget);
        comboBoxActionProportion->setObjectName(QStringLiteral("comboBoxActionProportion"));
        comboBoxActionProportion->setEditable(false);
        verticalLayoutMidButtons->addWidget(comboBoxActionProportion);
        horizontalLayoutMain->addLayout(verticalLayoutMidButtons);

        verticalLayoutResult = new QVBoxLayout();
        verticalLayoutResult->setObjectName(QStringLiteral("verticalLayoutResult"));
        labelCustomize = new QLabel(customizeWidget);
        labelCustomize->setObjectName(QStringLiteral("labelCustomize"));

        verticalLayoutResult->addWidget(labelCustomize);

        horizontalLayoutCategorySelect = new QHBoxLayout();
        horizontalLayoutCategorySelect->setObjectName(QStringLiteral("horizontalLayoutCategorySelect"));
        radioButtonMainCategory = new QRadioButton(customizeWidget);
        radioButtonMainCategory->setObjectName(QStringLiteral("radioButtonMainCategory"));
        radioButtonMainCategory->setChecked(false);

        horizontalLayoutCategorySelect->addWidget(radioButtonMainCategory);

        radioButtonAllCategory = new QRadioButton(customizeWidget);
        radioButtonAllCategory->setObjectName(QStringLiteral("radioButtonAllCategory"));
        radioButtonAllCategory->setChecked(true);

        horizontalLayoutCategorySelect->addWidget(radioButtonAllCategory);

        radioButtonGroup = new QButtonGroup(customizeWidget);
        radioButtonGroup->addButton(radioButtonMainCategory);
        radioButtonGroup->addButton(radioButtonAllCategory);

        verticalLayoutResult->addLayout(horizontalLayoutCategorySelect);

        treeViewResult = new QTreeView(customizeWidget);
        treeViewResult->setObjectName(QStringLiteral("treeViewResult"));
        treeViewResult->setHeaderHidden(true);
        treeViewResult->setSelectionMode(QAbstractItemView::SingleSelection);
        treeViewResult->setAnimated(true);                                   // 支持动画
        treeViewResult->setEditTriggers(QAbstractItemView::NoEditTriggers);  // 不允许直接在item上重命名

        verticalLayoutResult->addWidget(treeViewResult);

        horizontalLayoutActionOptBtns = new QHBoxLayout();
        horizontalLayoutActionOptBtns->setObjectName(QStringLiteral("horizontalLayoutActionOptBtns"));
        pushButtonNewCategory = new QPushButton(customizeWidget);
        pushButtonNewCategory->setObjectName(QStringLiteral("pushButtonNewCategory"));

        horizontalLayoutActionOptBtns->addWidget(pushButtonNewCategory);

        pushButtonNewPanel = new QPushButton(customizeWidget);
        pushButtonNewPanel->setObjectName(QStringLiteral("pushButtonNewPanel"));

        horizontalLayoutActionOptBtns->addWidget(pushButtonNewPanel);

        pushButtonRename = new QPushButton(customizeWidget);
        pushButtonRename->setObjectName(QStringLiteral("pushButtonRename"));

        horizontalLayoutActionOptBtns->addWidget(pushButtonRename);

        pushButtonReset = new QPushButton(customizeWidget);
        pushButtonReset->setObjectName(QStringLiteral("pushButtonReset"));
        horizontalLayoutActionOptBtns->addWidget(pushButtonReset);

        verticalLayoutResult->addLayout(horizontalLayoutActionOptBtns);

        horizontalLayoutMain->addLayout(verticalLayoutResult);

        verticalLayoutRightButtons = new QVBoxLayout();
        verticalLayoutRightButtons->setObjectName(QStringLiteral("verticalLayoutRightButtons"));
        verticalSpacerUp2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayoutRightButtons->addItem(verticalSpacerUp2);

        toolButtonUp = new QToolButton(customizeWidget);
        toolButtonUp->setObjectName(QStringLiteral("pushButtonUp"));
        toolButtonUp->setArrowType(Qt::UpArrow);
        toolButtonUp->setAutoRaise(true);

        verticalLayoutRightButtons->addWidget(toolButtonUp);

        toolButtonDown = new QToolButton(customizeWidget);
        toolButtonDown->setObjectName(QStringLiteral("pushButtonDown"));
        toolButtonDown->setArrowType(Qt::DownArrow);
        toolButtonDown->setAutoRaise(true);

        verticalLayoutRightButtons->addWidget(toolButtonDown);

        verticalSpacerDown2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayoutRightButtons->addItem(verticalSpacerDown2);

        horizontalLayoutMain->addLayout(verticalLayoutRightButtons);

        retranslateUi(customizeWidget);
    }  // setupUi

    void retranslateUi(QWidget* customizeWidget)
    {
        customizeWidget->setWindowTitle(QApplication::translate("SARibbonCustomizeWidget", "Customize Widget", Q_NULLPTR));
        labelSelectAction->setText(QApplication::translate("SARibbonCustomizeWidget", "Please Select", Q_NULLPTR));  // cn:请选择
        lineEditSearchAction->setInputMask(QString());
        lineEditSearchAction->setText(QString());
        lineEditSearchAction->setPlaceholderText(
            QApplication::translate("SARibbonCustomizeWidget", "Find Command", Q_NULLPTR));  // cn:查找命令
        pushButtonAdd->setText(QApplication::translate("SARibbonCustomizeWidget", "Add >>", Q_NULLPTR));  // cn:添加 >>
        pushButtonDelete->setText(QApplication::translate("SARibbonCustomizeWidget", "<< Remove", Q_NULLPTR));  // cn:<< 移除
        labelCustomize->setText(
            QApplication::translate("SARibbonCustomizeWidget", "Customize the Ribbon", Q_NULLPTR));  // cn:自定义功能区
        radioButtonMainCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "Main Category", Q_NULLPTR));  // cn:主选项卡
        radioButtonAllCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "All Category", Q_NULLPTR));  // cn:所有选项卡
        pushButtonNewCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "New Category", Q_NULLPTR));  // cn:新建选项卡
        pushButtonNewPanel->setText(QApplication::translate("SARibbonCustomizeWidget", "New Group", Q_NULLPTR));  // cn:新建组
        pushButtonRename->setText(QApplication::translate("SARibbonCustomizeWidget", "Rename", Q_NULLPTR));  // cn:重命名
        pushButtonReset->setText(QApplication::translate("SARibbonCustomizeWidget", "reset", Q_NULLPTR));  // cn:重置
        labelProportion->setText(QApplication::translate("SARibbonCustomizeWidget", "proportion:", Q_NULLPTR));  // cn:比例
    }  // retranslateUi
};

/**
 * @brief 管理SARibbonCustomizeWidget的业务逻辑
 */
class SARibbonCustomizeWidget::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonCustomizeWidget)
public:
    SARibbonCustomizeWidget::RibbonTreeShowType mShowType { SARibbonCustomizeWidget::ShowAllCategory };  ///< 显示类型
    SARibbonBar* mRibbonBar { nullptr };                   ///< 保存SARibbonMainWindow的指针
    SARibbonActionsManager* mActionMgr { nullptr };        ///< action管理器
    SARibbonActionsManagerModel* mAcionModel { nullptr };  ///< action管理器对应的model
    QStandardItemModel* mRibbonModel { nullptr };          ///< 用于很成ribbon的树
    int mCustomizeCategoryCount { 0 };                     ///< 记录自定义Category的个数
    int mCustomizePanelCount { 0 };                        ///< 记录自定义Panel的个数
public:
    PrivateData(SARibbonCustomizeWidget* p);
    void updateModel();

    QList< SARibbonCustomizeData > mCustomizeDatasCache;    ///< 缓存记录所有的自定义动作
    QList< SARibbonCustomizeData > mCustomizeDatasApplied;  ///< 应用后的所有的自定义动作
    QList< SARibbonCustomizeData > mOldCustomizeDatas;      ///< 记录旧的自定义动作,本地文件缓存
    // 创建一个随机id，形如：pre_QDateTime::currentMSecsSinceEpoch_suf
    static QString makeRandomObjName(const QString& pre);

    int itemLevel(QStandardItem* item) const;

    //
    bool isCustomizeItem(QStandardItem* item) const;

    // 把item转换为category
    SARibbonCategory* itemToCategory(QStandardItem* item) const;

    // 把item转换为SARibbonPanel
    SARibbonPanel* itemToPanel(QStandardItem* item) const;

    // 获取item对应的object name
    QString itemObjectName(QStandardItem* item) const;

    // 判断是否可以自定义
    bool isItemCanCustomize(QStandardItem* item) const;

    // 从item转为action
    QAction* itemToAction(QStandardItem* item) const;
};

SARibbonCustomizeWidget::PrivateData::PrivateData(SARibbonCustomizeWidget* p)
    : q_ptr(p), mAcionModel(new SARibbonActionsManagerModel(p)), mRibbonModel(new QStandardItemModel(p))
{
}

void SARibbonCustomizeWidget::PrivateData::updateModel()
{
    if (mRibbonBar == nullptr) {
        return;
    }
    mRibbonModel->clear();
    SARibbonBar* ribbonbar               = mRibbonBar;
    QList< SARibbonCategory* > categorys = ribbonbar->categoryPages();

    for (const SARibbonCategory* c : qAsConst(categorys)) {
        if ((mShowType == SARibbonCustomizeWidget::ShowMainCategory) && c->isContextCategory()) {
            // 如果是只显示主内容，如果是上下文标签就忽略
            continue;
        }
        QStandardItem* ci = new QStandardItem();
        if (c->isContextCategory()) {
            ci->setText(QString("[%1]").arg(c->categoryName()));
        } else {
            ci->setText(c->categoryName());
        }
        if (c->isCanCustomize() && !c->isContextCategory()) {
            // 上下文标签不做显示隐藏处理
            ci->setCheckable(true);
            ci->setCheckState(ribbonbar->isCategoryVisible(c) ? Qt::Checked : Qt::Unchecked);
            ci->setData(true, SARibbonCustomizeWidget::CanCustomizeRole);  // 标记这个是可以自定义的
        }
        ci->setData(0, SARibbonCustomizeWidget::LevelRole);
        ci->setData(QVariant::fromValue< qintptr >(qintptr(c)), SARibbonCustomizeWidget::PointerRole);
        QList< SARibbonPanel* > panels = c->panelList();
        for (const SARibbonPanel* p : qAsConst(panels)) {
            QStandardItem* pi = new QStandardItem(p->panelName());
            pi->setData(1, SARibbonCustomizeWidget::LevelRole);
            pi->setData(QVariant::fromValue< qintptr >(qintptr(p)), SARibbonCustomizeWidget::PointerRole);
            if (p->isCanCustomize()) {
                pi->setData(true, SARibbonCustomizeWidget::CanCustomizeRole);  // 标记这个是可以自定义的
            }
            ci->appendRow(pi);
            const QList< SARibbonPanelItem* >& items = p->ribbonPanelItem();
            for (SARibbonPanelItem* i : qAsConst(items)) {
                if (i->action->isSeparator()) {
                    continue;
                }
                QStandardItem* ii = new QStandardItem();
                //                if (i->customWidget) {
                //                    //如果是自定义窗口
                //                    if (i->widget()->windowTitle().isEmpty() && i->widget()->windowIcon().isNull()) {
                //                        delete ii;
                //                        continue;  //如果窗口啥也没有，就跳过
                //                    }
                //                    ii->setText(i->widget()->windowTitle());
                //                    ii->setIcon(i->widget()->windowIcon());
                //                    if (SARibbonCustomizeData::isCanCustomize(i->widget())) {
                //                        ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的
                //                    }
                //                } else {
                //                    //不是自定义，说明是action
                //                    ii->setText(i->action->text());
                //                    ii->setIcon(i->action->icon());
                //                    if (SARibbonCustomizeData::isCanCustomize(i->action)) {
                //                        ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的
                //                    }
                //                }
                ii->setText(i->action->text());
                ii->setIcon(i->action->icon());
                if (SARibbonCustomizeData::isCanCustomize(i->action)) {
                    ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole);  // 标记这个是可以自定义的
                }
                ii->setData(2, SARibbonCustomizeWidget::LevelRole);
                ii->setData(QVariant::fromValue< qintptr >(qintptr(i)), SARibbonCustomizeWidget::PointerRole);
                pi->appendRow(ii);
            }
        }
        mRibbonModel->appendRow(ci);
    }
}

/**
 * @brief 创建一个随机id，形如：pre_QDateTime::currentMSecsSinceEpoch
 * @param pre 前缀
 * @return
 */
QString SARibbonCustomizeWidget::PrivateData::makeRandomObjName(const QString& pre)
{
    return (QString("%1_%2").arg(pre).arg(QDateTime::currentMSecsSinceEpoch()));
}

/**
 * @brief 获取item的level
 * @param item
 * @return
 */
int SARibbonCustomizeWidget::PrivateData::itemLevel(QStandardItem* item) const
{
    return (item->data(SARibbonCustomizeWidget::LevelRole).toInt());
}

/**
 * @brief 判断itemn为自定义的item，自定义的item都带有CustomizeRole角色
 * @param item
 * @return
 */
bool SARibbonCustomizeWidget::PrivateData::isCustomizeItem(QStandardItem* item) const
{
    if (nullptr == item) {
        return (false);
    }
    return (item->data(SARibbonCustomizeWidget::CustomizeRole).isValid());
}

/**
 * @brief 把item转换为category
 * @param item
 * @return无法转换返回nullptr
 */
SARibbonCategory* SARibbonCustomizeWidget::PrivateData::itemToCategory(QStandardItem* item) const
{
    int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt();

    if (level != 0) {
        return (nullptr);
    }
    qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >();

    return (reinterpret_cast< SARibbonCategory* >(p));
}

/**
 * @brief 把item转换为panel
 * @param item
 * @return 无法转换返回nullptr
 */
SARibbonPanel* SARibbonCustomizeWidget::PrivateData::itemToPanel(QStandardItem* item) const
{
    int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt();

    if (level != 1) {
        return (nullptr);
    }
    qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >();

    return (reinterpret_cast< SARibbonPanel* >(p));
}

/**
 * @brief 获取item对应的objectname
 * @param item
 * @return 如果无法获取，返回一个空的QString
 */
QString SARibbonCustomizeWidget::PrivateData::itemObjectName(QStandardItem* item) const
{
    QString objName;

    if (isCustomizeItem(item)) {
        // 说明是自定义的
        objName = item->data(SARibbonCustomizeWidget::CustomizeObjNameRole).toString();
    } else {
        // 说明这个是非自定义的
        int level = itemLevel(item);
        if (0 == level) {
            SARibbonCategory* category = itemToCategory(item);
            if (category) {
                objName = category->objectName();
            }
        } else if (1 == level) {
            SARibbonPanel* panel = itemToPanel(item);
            if (panel) {
                objName = panel->objectName();
            }
        }
    }
    return (objName);
}

/**
 * @brief 判断item是否可自定义
 * @param item
 * @return
 */
bool SARibbonCustomizeWidget::PrivateData::isItemCanCustomize(QStandardItem* item) const
{
    if (nullptr == item) {
        return (false);
    }
    QVariant v = item->data(SARibbonCustomizeWidget::CanCustomizeRole);

    if (v.isValid()) {
        return (v.toBool());
    }
    return (false);
}

/**
 * @brief 从item 转为action
 * @param item
 * @return
 */
QAction* SARibbonCustomizeWidget::PrivateData::itemToAction(QStandardItem* item) const
{
    if (2 != itemLevel(item)) {
        return (nullptr);
    }
    // 这里要非常注意，SARibbonCustomizeWidget::CustomizeRole为true时，说明这个是自定义的内容，
    // 这时PointerRole里存放的是action指针，不是SARibbonPanelItem
    QAction* act = nullptr;

    if (item->data(SARibbonCustomizeWidget::CustomizeRole).toBool()) {
        act = reinterpret_cast< QAction* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >());
    } else {
        SARibbonPanelItem* pi =
            reinterpret_cast< SARibbonPanelItem* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >());
        act = (pi->action);
    }
    return (act);
}

//===================================================
// SARibbonCustomizeWidget
//===================================================
/**
 * @brief SARibbonCustomizeWidget::SARibbonCustomizeWidget
 * @param ribbonWindow 传入需要管理的SARibbonMainWindow指针
 * @param parent 用于界面生成的parent，可以和SARibbonMainWindow一样
 * @param f 同QWidget::QWidget的第二个参数
 */
SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent, Qt::WindowFlags f)
    : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi)
{
    init(ribbonWindow->ribbonBar());
}

SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent, Qt::WindowFlags f)
    : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi)
{
    init(ribbonbar);
}

void SARibbonCustomizeWidget::init(SARibbonBar* ribbonbar)
{
    d_ptr->mRibbonBar = ribbonbar;
    ui->setupUi(this);
    ui->listViewSelect->setModel(d_ptr->mAcionModel);
    ui->treeViewResult->setModel(d_ptr->mRibbonModel);
    initConnection();
    updateModel();
}

SARibbonCustomizeWidget::~SARibbonCustomizeWidget()
{
    delete ui;
}

void SARibbonCustomizeWidget::initConnection()
{
    //    这个需要qt5.8以上支持
    //    connect(ui->comboBoxActionIndex, QOverload<int>::of(&QComboBox::currentIndexChanged)
    //        , this, &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged);
    connect(ui->comboBoxActionIndex,
            static_cast< void (QComboBox::*)(int) >(&QComboBox::currentIndexChanged),
            this,
            &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged);
    //    这个需要qt5.8以上支持
    //    connect(ui->radioButtonGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked)
    //        , this, &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked);
    connect(ui->radioButtonGroup,
            static_cast< void (QButtonGroup::*)(QAbstractButton*) >(&QButtonGroup::buttonClicked),
            this,
            &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked);
    connect(ui->pushButtonNewCategory, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewCategoryClicked);
    connect(ui->pushButtonNewPanel, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewPanelClicked);
    connect(ui->pushButtonRename, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonRenameClicked);
    connect(ui->pushButtonAdd, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonAddClicked);
    connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonDeleteClicked);
    connect(ui->listViewSelect, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onListViewSelectClicked);
    connect(ui->treeViewResult, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onTreeViewResultClicked);
    connect(ui->toolButtonUp, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonUpClicked);
    connect(ui->toolButtonDown, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonDownClicked);
    connect(d_ptr->mRibbonModel, &QStandardItemModel::itemChanged, this, &SARibbonCustomizeWidget::onItemChanged);
    connect(ui->lineEditSearchAction, &QLineEdit::textEdited, this, &SARibbonCustomizeWidget::onLineEditSearchActionTextEdited);
    connect(ui->pushButtonReset, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonResetClicked);
}

/**
 * @brief 设置action管理器
 * @param mgr
 */
void SARibbonCustomizeWidget::setupActionsManager(SARibbonActionsManager* mgr)
{
    d_ptr->mActionMgr = mgr;
    if (d_ptr->mActionMgr) {
        d_ptr->mAcionModel->uninstallActionsManager();
    }
    d_ptr->mAcionModel->setupActionsManager(mgr);
    // 更新左边复选框
    QList< int > tags = mgr->actionTags();

    ui->comboBoxActionIndex->clear();
    for (int tag : qAsConst(tags)) {
        if (mgr->tagName(tag).isEmpty())
            continue;
        ui->comboBoxActionIndex->addItem(mgr->tagName(tag), tag);
    }
}

/**
 * @brief //判断用户是否有要存储的内容，对应save动作
 * @return
 */
bool SARibbonCustomizeWidget::isApplied() const
{
    return (d_ptr->mCustomizeDatasApplied.size() > 0);
}

/**
 * @brief 判断用户是否有改动内容，对应apply动作
 * @return
 */
bool SARibbonCustomizeWidget::isCached() const
{
    return (d_ptr->mCustomizeDatasCache.size() > 0);
}

/**
 * @brief 获取model
 * @return
 */
const QStandardItemModel* SARibbonCustomizeWidget::model() const
{
    return (d_ptr->mRibbonModel);
}

/**
 * @brief 根据当前的radiobutton选项来更新model
 */
void SARibbonCustomizeWidget::updateModel()
{
    updateModel(ui->radioButtonAllCategory->isChecked() ? ShowAllCategory : ShowMainCategory);
    if (d_ptr->mRibbonBar) {
        SARibbonBar* bar = d_ptr->mRibbonBar;
        if (bar) {
            ui->comboBoxActionProportion->clear();
            if (bar->isTwoRowStyle()) {
                ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPanelItem::Large);
                ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPanelItem::Small);
            } else {
                ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPanelItem::Large);
                ui->comboBoxActionProportion->addItem(tr("medium"), SARibbonPanelItem::Medium);
                ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPanelItem::Small);
            }
        }
    }
}

/**
 * @brief 更新model
 */
void SARibbonCustomizeWidget::updateModel(RibbonTreeShowType type)
{
    d_ptr->mShowType = type;
    d_ptr->updateModel();
}

/**
 * @brief 应用所有设定
 * @return 应用成功返回true
 * @note 所有设定有一个应用成功都会返回true
 */
bool SARibbonCustomizeWidget::applys()
{
    simplify();
    if (sa_customize_datas_apply(d_ptr->mCustomizeDatasCache, d_ptr->mRibbonBar) > 0) {
        // 将临时操作存入已应用操作，并清空临时操作
        makeActionsApplied();
        clearCache();
        return true;
    } else {
        return false;
    }
}

/**
 * @brief 转换为xml
 *
 * 此函数仅会写element，不会写document相关内容，因此如果需要写document，
 * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument()
 *
 * @note 注意，在传入QXmlStreamWriter之前，需要设置编码为utf-8:xml->setCodec("utf-8");
 * @note 由于QXmlStreamWriter在QString作为io时，是不支持编码的，而此又无法保证自定义过程不出现中文字符，
 * 因此，QXmlStreamWriter不应该通过QString进行构造，如果需要用到string，也需要通过QByteArray构造，如：
 * @code
 * SARibbonCustomizeDialog dlg(this);//this为SARibbonMainWindow的窗口
 * dlg.setupActionsManager(m_actMgr);
 * if (SARibbonCustomizeDialog::Accepted == dlg.exec()) {
 *    dlg.applys();
 *    QByteArray str;
 *    QXmlStreamWriter xml(&str);//QXmlStreamWriter不建议通过QString构造，遇到中文会异常
 *    xml.setAutoFormatting(true);
 *    xml.setAutoFormattingIndent(2);
 *    xml.setCodec("utf-8");//在writeStartDocument之前指定编码
 *    xml.writeStartDocument();
 *    bool isok = dlg.toXml(&xml);
 *    xml.writeEndDocument();
 *    if (isok) {
 *        QFile f("customize.xml");
 *        if (f.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) {
 *            QTextStream s(&f);
 *            s.setCodec("utf-8");//指定编码输出
 *            s << str;
 *            s.flush();
 *        }
 *        m_edit->append("write xml:");//m_edit的定义为：QTextEdit *m_edit;
 *        m_edit->append(str);
 *    }
 * }
 * @endcode
 * @return 如果出现异常，返回false,如果没有自定义数据也会返回false
 * @see sa_customize_datas_to_xml
 */
bool SARibbonCustomizeWidget::toXml(QXmlStreamWriter* xml) const
{
    QList< SARibbonCustomizeData > res = d_ptr->mOldCustomizeDatas;

    if (isApplied())
        res << d_ptr->mCustomizeDatasApplied;
    if (isCached())
        res << d_ptr->mCustomizeDatasCache;

    res = SARibbonCustomizeData::simplify(res);
    return (sa_customize_datas_to_xml(xml, res));
}

/**
 * @brief 把配置写入文件中
 * @param xmlpath
 * @return
 */
bool SARibbonCustomizeWidget::toXml(const QString& xmlpath) const
{
    QFile f(xmlpath);

    if (!f.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
        return (false);
    }
    QXmlStreamWriter xml(&f);

    xml.setAutoFormatting(true);
    xml.setAutoFormattingIndent(2);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)  // QXmlStreamWriter always encodes XML in UTF-8.
    xml.setCodec("utf-8");                  // 在writeStartDocument之前指定编码
#endif
    xml.writeStartDocument();
    bool isOK = toXml(&xml);

    xml.writeEndDocument();
    f.close();
    return (isOK);
}

/**
 * @brief 从xml中加载QList<SARibbonCustomizeData>
 *
 * 对于基于配置文件的设置，对话框显示前建议调用此函数，保证叠加设置的正确记录
 * @param xml
 * @return
 * @note 此函数要在@ref setupActionsManager 函数之后调用
 */
void SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml)
{
    QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, d_ptr->mActionMgr);

    d_ptr->mOldCustomizeDatas = cds;
}

/**
 * @brief 从xml中加载QList<SARibbonCustomizeData>
 *
 * 对于基于配置文件的设置，对话框显示前建议调用此函数，保证叠加设置的正确记录
 * @param xmlpath
 * @note 此函数要在@ref setupActionsManager 函数之后调用
 * @note 如果程序启动后加载了自定义配置，再调用此窗口时需要调用此函数，把原来的配置加载进来，
 * 在生成新动作时会把旧动作保存，但在调用applys时不会调用此加载的动作
 */
void SARibbonCustomizeWidget::fromXml(const QString& xmlpath)
{
    QFile f(xmlpath);

    if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return;
    }
    f.seek(0);
    QXmlStreamReader xml(&f);

    fromXml(&xml);
}

/**
 * @brief 应用xml配置
 *
 * @note 重复加载一个配置文件会发生异常，为了避免此类事件发生，一般通过一个变量保证只加载一次，如：
 * @code
 * //只能调用一次
 * static bool has_call = false;
 * if (!has_call) {
 *     QFile f("customize.xml");
 *     if (!f.open(QIODevice::ReadWrite|QIODevice::Text)) {
 *         return;
 *     }
 *     f.seek(0);
 *     QXmlStreamReader xml(&f);
 *     has_call = SARibbonCustomizeWidget::fromXml(&xml, this, m_actMgr);
 * }
 * @endcode
 * @param xml
 * @param bar SARibbonBar
 * @return 所有设定有一个应用成功都会返回true
 * @see sa_customize_datas_from_xml sa_customize_datas_apply sa_apply_customize_from_xml_file
 */
bool SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr)
{
    // 先找到sa-ribbon-customize标签
    QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, mgr);

    return (sa_customize_datas_apply(cds, bar) > 0);
}

/**
 * @brief 清除已应用的动作
 */
void SARibbonCustomizeWidget::makeActionsApplied()
{
    d_ptr->mCustomizeDatasApplied << d_ptr->mCustomizeDatasCache;
}

/**
 * @brief 清除applied的动作，cancel操作后需要清空已应用的动作
 */
void SARibbonCustomizeWidget::clearApplied()
{
    d_ptr->mCustomizeDatasApplied.clear();
}

/**
 * @brief 清除缓存动作
 *
 * 在执行applys函数后，如果要继续调用，应该clear，否则会导致异常
 */
void SARibbonCustomizeWidget::clearCache()
{
    d_ptr->mCustomizeDatasCache.clear();
}

/**
 * @brief 清除所有动作，不包含本地读取的数据
 */
void SARibbonCustomizeWidget::clear()
{
    clearApplied();
    clearCache();
}

/**
 * @brief 精简
 */
void SARibbonCustomizeWidget::simplify()
{
    d_ptr->mCustomizeDatasCache = SARibbonCustomizeData::simplify(d_ptr->mCustomizeDatasCache);
}

/**
 * @brief 获取当前界面选中的行属性
 * @return
 */
SARibbonPanelItem::RowProportion SARibbonCustomizeWidget::selectedRowProportion() const
{
    return (static_cast< SARibbonPanelItem::RowProportion >(ui->comboBoxActionProportion->currentData().toInt()));
}

/**
 * @brief 获取listview中选中的action
 * @return 如果没有选中action，返回nullptr
 * @note 如果要获取treeview选中的action，使用@ref itemToAction 函数
 */
QAction* SARibbonCustomizeWidget::selectedAction() const
{
    QItemSelectionModel* m = ui->listViewSelect->selectionModel();

    if ((nullptr == m) || !m->hasSelection()) {
        return (nullptr);
    }
    QModelIndex i = m->currentIndex();

    return (d_ptr->mAcionModel->indexToAction(i));
}

/**
 * @brief 把item转换为action
 * @param item
 * @return 如果没有action可转换，返回nullptr
 */
QAction* SARibbonCustomizeWidget::itemToAction(QStandardItem* item) const
{
    return (d_ptr->itemToAction(item));
}

/**
 * @brief 获取ribbon tree选中的item
 * @return
 */
QStandardItem* SARibbonCustomizeWidget::selectedItem() const
{
    QItemSelectionModel* m = ui->treeViewResult->selectionModel();

    if ((nullptr == m) || !m->hasSelection()) {
        return (nullptr);
    }
    QModelIndex i = m->currentIndex();

    return (d_ptr->mRibbonModel->itemFromIndex(i));
}

/**
 * @brief 获取选中的ribbon tree 的level
 * @return -1为选中异常，0代表选中了category 1代表选中了panel 2代表选中了action
 */
int SARibbonCustomizeWidget::selectedRibbonLevel() const
{
    QStandardItem* item = selectedItem();

    if (item) {
        return (itemLevel(item));
    }
    return (-1);
}

/**
 * @brief 获取StandardItem 的level
 * @param item
 * @return
 */
int SARibbonCustomizeWidget::itemLevel(QStandardItem* item) const
{
    return (d_ptr->itemLevel(item));
}

/**
 * @brief 设置某个item被选中
 * @param item
 */
void SARibbonCustomizeWidget::setSelectItem(QStandardItem* item, bool ensureVisible)
{
    QItemSelectionModel* m = ui->treeViewResult->selectionModel();

    if (nullptr == m) {
        return;
    }
    if (m) {
        m->clearSelection();
        m->setCurrentIndex(item->index(), QItemSelectionModel::SelectCurrent);
    }
    if (ensureVisible) {
        ui->treeViewResult->scrollTo(item->index());
    }
}

/**
 * @brief 判断itemn能否改动，可以改动返回true
 * @param item
 * @return
 */
bool SARibbonCustomizeWidget::isItemCanCustomize(QStandardItem* item) const
{
    return (d_ptr->isItemCanCustomize(item));
}

bool SARibbonCustomizeWidget::isSelectedItemCanCustomize() const
{
    return (isItemCanCustomize(selectedItem()));
}

/**
 * @brief 判断itemn能否改动，可以改动返回true
 * @param item
 * @return
 */
bool SARibbonCustomizeWidget::isCustomizeItem(QStandardItem* item) const
{
    return (d_ptr->isCustomizeItem(item));
}

bool SARibbonCustomizeWidget::isSelectedItemIsCustomize() const
{
    return (isCustomizeItem(selectedItem()));
}

void SARibbonCustomizeWidget::removeItem(QStandardItem* item)
{
    if (item->parent()) {
        item->parent()->removeRow(item->row());
    } else {
        d_ptr->mRibbonModel->removeRow(item->row());
    }
}

void SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged(int index)
{
    int tag = ui->comboBoxActionIndex->itemData(index).toInt();

    d_ptr->mAcionModel->setFilter(tag);
}

void SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked(QAbstractButton* b)
{
    updateModel(b == ui->radioButtonAllCategory ? ShowAllCategory : ShowMainCategory);
}

void SARibbonCustomizeWidget::onPushButtonNewCategoryClicked()
{
    int row                = d_ptr->mRibbonModel->rowCount();
    QItemSelectionModel* m = ui->treeViewResult->selectionModel();

    if (m && m->hasSelection()) {
        QModelIndex i = m->currentIndex();
        while (i.parent().isValid()) {
            i = i.parent();
        }
        // 获取选中的最顶层item
        row = i.row() + 1;
    }
    QStandardItem* ni = new QStandardItem(tr("new category[customize]%1").arg(++(d_ptr->mCustomizeCategoryCount)));

    ni->setData(0, SARibbonCustomizeWidget::LevelRole);
    d_ptr->mRibbonModel->insertRow(row, ni);
    // 设置新增的为选中
    setSelectItem(ni);
    // 把动作插入动作列表中
    SARibbonCustomizeData d = SARibbonCustomizeData::makeAddCategoryCustomizeData(
        ni->text(), ni->row(), SARibbonCustomizeWidget::PrivateData::makeRandomObjName("category"));

    d_ptr->mCustomizeDatasCache.append(d);
    ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole);  // 有CustomizeRole，必有CanCustomizeRole
    ni->setData(true, SARibbonCustomizeWidget::CustomizeRole);
    ni->setData(d.categoryObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole);
}

void SARibbonCustomizeWidget::onPushButtonNewPanelClicked()
{
    QStandardItem* item = selectedItem();

    if (nullptr == item) {
        return;
    }
    int level = selectedRibbonLevel();

    QStandardItem* ni = new QStandardItem(tr("new panel[customize]%1").arg(++(d_ptr->mCustomizePanelCount)));

    ni->setData(1, SARibbonCustomizeWidget::LevelRole);

    if (0 == level) {
        // 说明是category,插入到最后
        item->appendRow(ni);
    } else if (1 == level) {
        // 说明选择的是panel，插入到此panel之后
        QStandardItem* categoryItem = item->parent();
        if (nullptr == categoryItem) {
            return;
        }
        categoryItem->insertRow(item->row() + 1, ni);
    } else {
        // 不符合就删除退出
        delete ni;
        ni = nullptr;
        return;
    }
    // 查找category的object name
    QStandardItem* categoryItem = ni->parent();
    QString categoryObjName     = "";

    categoryObjName         = d_ptr->itemObjectName(categoryItem);
    SARibbonCustomizeData d = SARibbonCustomizeData::makeAddPanelCustomizeData(
        ni->text(), ni->row(), categoryObjName, SARibbonCustomizeWidget::PrivateData::makeRandomObjName("panel"));

    d_ptr->mCustomizeDatasCache.append(d);
    ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole);  // 有CustomizeRole，必有CanCustomizeRole
    ni->setData(true, SARibbonCustomizeWidget::CustomizeRole);
    ni->setData(d.panelObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole);
    setSelectItem(ni);
}

void SARibbonCustomizeWidget::onPushButtonRenameClicked()
{
    QStandardItem* item = selectedItem();

    if (nullptr == item) {
        return;
    }
    bool ok;
    QString text = "";

    text = QInputDialog::getText(this, tr("rename"), tr("name:"), QLineEdit::Normal, item->text(), &ok);

    if (!ok || text.isEmpty()) {
        return;
    }
    int level = itemLevel(item);

    if (0 == level) {
        // 改Category名
        QString cateObjName     = d_ptr->itemObjectName(item);
        SARibbonCustomizeData d = SARibbonCustomizeData::makeRenameCategoryCustomizeData(text, cateObjName);
        d_ptr->mCustomizeDatasCache.append(d);
    } else if (1 == level) {
        QString cateObjName     = d_ptr->itemObjectName(item->parent());
        QString panelObjName    = d_ptr->itemObjectName(item);
        SARibbonCustomizeData d = SARibbonCustomizeData::makeRenamePanelCustomizeData(text, cateObjName, panelObjName);
        d_ptr->mCustomizeDatasCache.append(d);
    } else {
        // action 不允许改名
        return;
    }
    item->setText(text);
}

void SARibbonCustomizeWidget::onPushButtonAddClicked()
{
    QAction* act        = selectedAction();
    QStandardItem* item = selectedItem();

    if ((nullptr == act) || (nullptr == item)) {
        return;
    }
    int level = itemLevel(item);

    if (0 == level) {
        // 选中category不进行操作
        return;
    } else if (2 == level) {
        // 选中action，添加到这个action之后,把item设置为panel
        item = item->parent();
    }
    QString panelObjName    = d_ptr->itemObjectName(item);
    QString categoryObjName = d_ptr->itemObjectName(item->parent());
    QString key             = d_ptr->mActionMgr->key(act);

    SARibbonCustomizeData d = SARibbonCustomizeData::makeAddActionCustomizeData(
        key, d_ptr->mActionMgr, selectedRowProportion(), categoryObjName, panelObjName);

    d_ptr->mCustomizeDatasCache.append(d);

    QStandardItem* actItem = new QStandardItem(act->icon(), act->text());

    actItem->setData(2, SARibbonCustomizeWidget::LevelRole);
    actItem->setData(true, SARibbonCustomizeWidget::CanCustomizeRole);  // 有CustomizeRole，必有CanCustomizeRole
    actItem->setData(true, SARibbonCustomizeWidget::CustomizeRole);
    actItem->setData(act->objectName(), SARibbonCustomizeWidget::CustomizeObjNameRole);
    actItem->setData(qintptr(act), SARibbonCustomizeWidget::PointerRole);  // 把action指针传入
    item->appendRow(actItem);
}

void SARibbonCustomizeWidget::onPushButtonDeleteClicked()
{
    QStandardItem* item = selectedItem();

    if (nullptr == item) {
        return;
    }
    if (!isItemCanCustomize(item)) {
        return;
    }
    int level = itemLevel(item);

    if (0 == level) {
        // 删除category
        SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveCategoryCustomizeData(d_ptr->itemObjectName(item));
        d_ptr->mCustomizeDatasCache.append(d);
    } else if (1 == level) {
        // 删除panel
        QString catObjName      = d_ptr->itemObjectName(item->parent());
        QString panelObjName    = d_ptr->itemObjectName(item);
        SARibbonCustomizeData d = SARibbonCustomizeData::makeRemovePanelCustomizeData(catObjName, panelObjName);
        d_ptr->mCustomizeDatasCache.append(d);
    } else if (2 == level) {
        // 删除Action
        QString catObjName   = d_ptr->itemObjectName(item->parent()->parent());
        QString panelObjName = d_ptr->itemObjectName(item->parent());
        QAction* act         = itemToAction(item);
        QString key          = d_ptr->mActionMgr->key(act);
        if (key.isEmpty() || catObjName.isEmpty() || panelObjName.isEmpty()) {
            return;
        }

        SARibbonCustomizeData d =
            SARibbonCustomizeData::makeRemoveActionCustomizeData(catObjName, panelObjName, key, d_ptr->mActionMgr);
        d_ptr->mCustomizeDatasCache.append(d);
    }
    // 执行删除操作
    removeItem(item);
    // 删除后重新识别
    ui->pushButtonAdd->setEnabled(selectedAction() && isSelectedItemIsCustomize() && selectedRibbonLevel() > 0);
    ui->pushButtonDelete->setEnabled(isSelectedItemIsCustomize());
}

void SARibbonCustomizeWidget::onListViewSelectClicked(const QModelIndex& index)
{
    // 每次点击，判断是否可以进行操作，决定pushButtonAdd和pushButtonDelete的显示状态
    // 点击了listview，判断treeview的状态
    Q_UNUSED(index);
    ui->pushButtonAdd->setEnabled(isSelectedItemCanCustomize() && selectedRibbonLevel() > 0);
    ui->pushButtonDelete->setEnabled(isSelectedItemCanCustomize());
}

void SARibbonCustomizeWidget::onTreeViewResultClicked(const QModelIndex& index)
{
    Q_UNUSED(index);
    // 每次点击，判断是否可以进行操作，决定pushButtonAdd和pushButtonDelete的显示状态
    QStandardItem* item = selectedItem();

    if (nullptr == item) {
        return;
    }
    int level = itemLevel(item);

    ui->pushButtonAdd->setEnabled(selectedAction() && (level > 0) && isItemCanCustomize(item));
    ui->pushButtonDelete->setEnabled(isItemCanCustomize(item));  // 有CustomizeRole，必有CanCustomizeRole
    ui->pushButtonRename->setEnabled(
        level != 2 || isItemCanCustomize(item));  // QAction 不能改名 ， 有CustomizeRole，必有CanCustomizeRole
}

void SARibbonCustomizeWidget::onToolButtonUpClicked()
{
    QStandardItem* item = selectedItem();

    if ((nullptr == item) || (0 == item->row())) {
        return;
    }
    int level = itemLevel(item);

    if (0 == level) {
        // 移动category
        SARibbonCustomizeData d =
            SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), -1);
        d_ptr->mCustomizeDatasCache.append(d);
        int r = item->row();
        item  = d_ptr->mRibbonModel->takeItem(r);
        d_ptr->mRibbonModel->removeRow(r);
        d_ptr->mRibbonModel->insertRow(r - 1, item);
    } else if (1 == level) {
        QStandardItem* paritem  = item->parent();
        SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePanelOrderCustomizeData(
            d_ptr->itemObjectName(paritem), d_ptr->itemObjectName(item), -1);
        d_ptr->mCustomizeDatasCache.append(d);
        int r = item->row();
        item  = paritem->takeChild(r);
        paritem->removeRow(r);
        paritem->insertRow(r - 1, item);
    } else if (2 == level) {
        QStandardItem* panelItem    = item->parent();
        QStandardItem* categoryItem = panelItem->parent();
        QAction* act                = itemToAction(item);
        if (!act) {
            return;
        }
        QString key             = d_ptr->mActionMgr->key(act);
        SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(
            d_ptr->itemObjectName(categoryItem), d_ptr->itemObjectName(panelItem), key, d_ptr->mActionMgr, -1);
        d_ptr->mCustomizeDatasCache.append(d);
        int r = item->row();
        item  = panelItem->takeChild(r);
        panelItem->removeRow(r);
        panelItem->insertRow(r - 1, item);
    }

    // 保持焦点，方便连续操作
    setSelectItem(item);
    onTreeViewResultClicked(item->index());
}

void SARibbonCustomizeWidget::onToolButtonDownClicked()
{
    QStandardItem* item = selectedItem();

    if (item == nullptr) {
        return;
    }
    int count = 0;

    if (item->parent()) {
        count = item->parent()->rowCount();
    } else {
        count = d_ptr->mRibbonModel->rowCount();
    }
    if ((nullptr == item) || ((count - 1) == item->row())) {
        return;
    }
    int level = itemLevel(item);

    if (0 == level) {
        // 移动category
        SARibbonCustomizeData d =
            SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), 1);
        d_ptr->mCustomizeDatasCache.append(d);
        int r = item->row();
        item  = d_ptr->mRibbonModel->takeItem(item->row());
        d_ptr->mRibbonModel->removeRow(r);
        d_ptr->mRibbonModel->insertRow(r + 1, item);
    } else if (1 == level) {
        QStandardItem* paritem  = item->parent();
        SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePanelOrderCustomizeData(
            d_ptr->itemObjectName(paritem), d_ptr->itemObjectName(item), 1);
        d_ptr->mCustomizeDatasCache.append(d);
        int r = item->row();
        item  = paritem->takeChild(r);
        paritem->removeRow(r);
        paritem->insertRow(r + 1, item);
    } else if (2 == level) {
        QStandardItem* panelItem    = item->parent();
        QStandardItem* categoryItem = panelItem->parent();
        QAction* act                = itemToAction(item);
        if (!act) {
            return;
        }
        QString key             = d_ptr->mActionMgr->key(act);
        SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(
            d_ptr->itemObjectName(categoryItem), d_ptr->itemObjectName(panelItem), key, d_ptr->mActionMgr, -1);
        d_ptr->mCustomizeDatasCache.append(d);
        int r = item->row();
        item  = panelItem->takeChild(r);
        panelItem->removeRow(r);
        panelItem->insertRow(r + 1, item);
    }

    // 保持焦点，方便连续操作
    setSelectItem(item);
    onTreeViewResultClicked(item->index());
}

void SARibbonCustomizeWidget::onItemChanged(QStandardItem* item)
{
    if (item == nullptr) {
        return;
    }
    int level = itemLevel(item);

    if (0 == level) {
        if (item->isCheckable()) {
            QString objname = d_ptr->itemObjectName(item);
            SARibbonCustomizeData d =
                SARibbonCustomizeData::makeVisibleCategoryCustomizeData(objname, item->checkState() == Qt::Checked);
            d_ptr->mCustomizeDatasCache.append(d);
        }
    }
}

void SARibbonCustomizeWidget::onLineEditSearchActionTextEdited(const QString& text)
{
    d_ptr->mAcionModel->search(text);
}

void SARibbonCustomizeWidget::onPushButtonResetClicked()
{
    int btn = QMessageBox::warning(this,
                                   tr("Warning"),
                                   tr("Are you sure reset all customize setting?"),
                                   QMessageBox::Yes | QMessageBox::No,
                                   QMessageBox::No);

    if (btn == QMessageBox::Yes) {
        clear();
    }
}

/*** End of inlined file: SARibbonCustomizeWidget.cpp ***/

/*** Start of inlined file: SARibbonCustomizeDialog.cpp ***/
#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include <QSpacerItem>

/**
 * @brief The SARibbonCustomizeDialogUi class
 */
class SARibbonCustomizeDialogUi
{
public:
    SARibbonCustomizeWidget* customWidget;
    QVBoxLayout* verticalLayoutMain;
    QHBoxLayout* horizontalLayoutButtonGroup;
    QPushButton* pushButtonCancel;
    QPushButton* pushButtonOk;
    QSpacerItem* spacerItemleft;
    void setupUi(SARibbonMainWindow* ribbonWindow, QWidget* customizeDialog)
    {
        if (customizeDialog->objectName().isEmpty()) {
            customizeDialog->setObjectName(QStringLiteral("SARibbonCustomizeDialog"));
        }
        customizeDialog->resize(800, 600);
        verticalLayoutMain = new QVBoxLayout(customizeDialog);
        verticalLayoutMain->setObjectName(QStringLiteral("verticalLayoutMain"));

        customWidget = new SARibbonCustomizeWidget(ribbonWindow, customizeDialog);
        customWidget->setObjectName(QStringLiteral("customWidget"));
        verticalLayoutMain->addWidget(customWidget);

        horizontalLayoutButtonGroup = new QHBoxLayout();
        horizontalLayoutButtonGroup->setObjectName(QStringLiteral("horizontalLayoutButtonGroup"));

        spacerItemleft = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
        horizontalLayoutButtonGroup->addItem(spacerItemleft);

        pushButtonCancel = new QPushButton(customizeDialog);
        pushButtonCancel->setObjectName(QStringLiteral("pushButtonCancel"));
        horizontalLayoutButtonGroup->addWidget(pushButtonCancel);

        pushButtonOk = new QPushButton(customizeDialog);
        pushButtonOk->setObjectName(QStringLiteral("pushButtonCancel"));
        horizontalLayoutButtonGroup->addWidget(pushButtonOk);
        verticalLayoutMain->addItem(horizontalLayoutButtonGroup);
        retranslateUi(customizeDialog);
    }

    void retranslateUi(QWidget* customizeDialog)
    {
        customizeDialog->setWindowTitle(QApplication::translate("SARibbonCustomizeDialog", "Customize Dialog", Q_NULLPTR));
        pushButtonCancel->setText(QApplication::translate("SARibbonCustomizeDialog", "Cancel", Q_NULLPTR));
        pushButtonOk->setText(QApplication::translate("SARibbonCustomizeDialog", "OK", Q_NULLPTR));
    }
};

////////////////////////////////////////////////////////////////////

SARibbonCustomizeDialog::SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, QWidget* p, Qt::WindowFlags f)
    : QDialog(p, f), ui(new SARibbonCustomizeDialogUi)
{
    ui->setupUi(ribbonWindow, this);
    initConnection();
}

/**
 * @brief 设置action管理器
 *
 * 等同@ref SARibbonCustomizeWidget::setupActionsManager
 * @param mgr
 */
SARibbonCustomizeDialog::~SARibbonCustomizeDialog()
{
}
void SARibbonCustomizeDialog::setupActionsManager(SARibbonActionsManager* mgr)
{
    ui->customWidget->setupActionsManager(mgr);
}

/**
 * @brief //判断用户是否有要存储的内容，对应save动作
 * @return
 */
bool SARibbonCustomizeDialog::isApplied() const
{
    return ui->customWidget->isApplied();
}

/**
 * @brief 判断用户是否有改动内容，对应apply动作
 * @return
 */
bool SARibbonCustomizeDialog::isCached() const
{
    return ui->customWidget->isCached();
}

void SARibbonCustomizeDialog::initConnection()
{
    connect(ui->pushButtonOk, &QPushButton::clicked, this, &QDialog::accept);
    connect(ui->pushButtonCancel, &QPushButton::clicked, this, &QDialog::reject);
}

/**
 * @brief 等同SARibbonCustomizeWidget::applys
 *
 * @ref SARibbonCustomizeWidget::applys
 * @return
 */
bool SARibbonCustomizeDialog::applys()
{
    return ui->customWidget->applys();
}

/**
 * @brief 清除所有动作
 *
 * @ref SARibbonCustomizeWidget::clear
 */
void SARibbonCustomizeDialog::clear()
{
    ui->customWidget->clear();
}

/**
 * @brief 转换为xml
 *
 * @ref SARibbonCustomizeWidget::toXml
 * @param xml
 * @return
 */
bool SARibbonCustomizeDialog::toXml(QXmlStreamWriter* xml) const
{
    return (ui->customWidget->toXml(xml));
}

/**
 * @brief 等同SARibbonCustomizeWidget::toXml
 * @ref SARibbonCustomizeWidget::toXml
 * @param xmlpath
 * @return
 */
bool SARibbonCustomizeDialog::toXml(const QString& xmlpath) const
{
    return (ui->customWidget->toXml(xmlpath));
}

/**
 * @brief 等同SARibbonCustomizeWidget::fromXml
 * @param xml
 */
void SARibbonCustomizeDialog::fromXml(QXmlStreamReader* xml)
{
    ui->customWidget->fromXml(xml);
}

/**
 * @brief 等同SARibbonCustomizeWidget::fromXml
 * @param xmlpath
 */
void SARibbonCustomizeDialog::fromXml(const QString& xmlpath)
{
    ui->customWidget->fromXml(xmlpath);
}

/**
 * @brief 返回SARibbonCustomizeWidget窗口指针
 *
 * 通过SARibbonCustomizeWidget窗口可以操作更多的内容
 *
 * @return SARibbonCustomizeWidget指针，参考@ref SARibbonCustomizeWidget
 */
SARibbonCustomizeWidget* SARibbonCustomizeDialog::customizeWidget() const
{
    return (ui->customWidget);
}

/*** End of inlined file: SARibbonCustomizeDialog.cpp ***/

/*** Start of inlined file: SARibbonMainWindow.cpp ***/
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QHash>
#include <QWindowStateChangeEvent>
#include <QScreen>

#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER
#include <QWKWidgets/widgetwindowagent.h>

#else

#endif

/**
 * @brief The SARibbonMainWindowPrivate class
 */
class SARibbonMainWindow::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonMainWindow)
public:
    PrivateData(SARibbonMainWindow* p);
    void installFrameless(SARibbonMainWindow* p);
    bool isUseRibbonBar() const;
    bool isUseRibbonFrame() const;
    bool isUseNativeFrame() const;
    void checkMainWindowFlag();
    static void updateTabBarMargins(SARibbonTabBar* tab, SARibbonTheme theme);
    static void updateContextColors(SARibbonBar* bar, SARibbonTheme theme);
    static void updateTabBarBaseLineColor(SARibbonBar* bar, SARibbonTheme theme);

public:
    SARibbonMainWindowStyles mRibbonMainWindowStyle;
    SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue };
    SARibbonSystemButtonBar* mWindowButtonGroup { nullptr };
#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER
    QWK::WidgetWindowAgent* mFramelessHelper { nullptr };
#else
    SAFramelessHelper* mFramelessHelper { nullptr };
#endif
    SARibbonMainWindowEventFilter* mEventFilter { nullptr };
};

SARibbonMainWindow::PrivateData::PrivateData(SARibbonMainWindow* p) : q_ptr(p)
{
}

void SARibbonMainWindow::PrivateData::installFrameless(SARibbonMainWindow* p)
{
#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER
    mFramelessHelper = new QWK::WidgetWindowAgent(p);
    mFramelessHelper->setup(p);
#else
    mFramelessHelper = new SAFramelessHelper(p);
#endif
}

bool SARibbonMainWindow::PrivateData::isUseRibbonBar() const
{
    return mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseRibbonMenuBar);
}

bool SARibbonMainWindow::PrivateData::isUseRibbonFrame() const
{
    return mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseRibbonFrame);
}

bool SARibbonMainWindow::PrivateData::isUseNativeFrame() const
{
    return mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseNativeFrame);
}

/**
 * @brief 检查flag的设置合理性
 */
void SARibbonMainWindow::PrivateData::checkMainWindowFlag()
{
    // 如果都没有设置边框样式，默认设置为ribbon边框
    if (!mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseRibbonFrame)
        && !mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseNativeFrame)) {
        mRibbonMainWindowStyle.setFlag(SARibbonMainWindowStyleFlag::UseRibbonFrame, true);
    }

    // 如果都没有设置MenuBar，默认设置为ribbonbar
    if (!mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseRibbonMenuBar)
        && !mRibbonMainWindowStyle.testFlag(SARibbonMainWindowStyleFlag::UseNativeMenuBar)) {
        mRibbonMainWindowStyle.setFlag(SARibbonMainWindowStyleFlag::UseRibbonMenuBar, true);
    }
}

void SARibbonMainWindow::PrivateData::updateTabBarMargins(SARibbonTabBar* tab, SARibbonTheme theme)
{
    static const std::map< SARibbonTheme, QMargins > themeMargins = {
        { SARibbonTheme::RibbonThemeWindows7, { 5, 0, 0, 0 } },
        { SARibbonTheme::RibbonThemeOffice2013, { 5, 0, 0, 0 } },
        { SARibbonTheme::RibbonThemeOffice2016Blue, { 5, 0, 0, 0 } },
        { SARibbonTheme::RibbonThemeDark, { 5, 0, 0, 0 } },
        { SARibbonTheme::RibbonThemeDark2, { 5, 0, 0, 0 } },
        { SARibbonTheme::RibbonThemeOffice2021Blue, { 5, 0, 5, 0 } }
    };
    auto it = themeMargins.find(theme);
    if (it != themeMargins.end()) {
        tab->setTabMargin(it->second);
    }
}

void SARibbonMainWindow::PrivateData::updateContextColors(SARibbonBar* bar, SARibbonTheme theme)
{
    static const SARibbonBar::FpContextCategoryHighlight cs_darkerHighlight = [](const QColor& c) -> QColor {
        return c.darker();
    };
    static const SARibbonBar::FpContextCategoryHighlight cs_vibrantHighlight = [](const QColor& c) -> QColor {
        return SA::makeColorVibrant(c);
    };

    switch (theme) {
    case SARibbonTheme::RibbonThemeWindows7:
    case SARibbonTheme::RibbonThemeOffice2013:
    case SARibbonTheme::RibbonThemeDark:
        bar->setContextCategoryColorList({});  // 重置为默认色系
        bar->setContextCategoryColorHighLight(cs_vibrantHighlight);
        break;
    case SARibbonTheme::RibbonThemeOffice2016Blue:
        bar->setContextCategoryColorList({ QColor(18, 64, 120) });
        bar->setContextCategoryColorHighLight(cs_darkerHighlight);
        break;
    case SARibbonTheme::RibbonThemeOffice2021Blue:
        bar->setContextCategoryColorList({ QColor(209, 207, 209) });
        bar->setContextCategoryColorHighLight([](const QColor& c) -> QColor { return QColor(39, 96, 167); });
        break;
    default:
        break;
    }
}

void SARibbonMainWindow::PrivateData::updateTabBarBaseLineColor(SARibbonBar* bar, SARibbonTheme theme)
{
    if (theme == SARibbonTheme::RibbonThemeOffice2013) {
        bar->setTabBarBaseLineColor(QColor(186, 201, 219));
    } else {
        bar->setTabBarBaseLineColor(QColor());
    }
}

//===================================================
// SARibbonMainWindow
//===================================================

/**
 * @brief 构造一个 SARibbonMainWindow 实例。
 *
 * 此构造函数初始化一个带有 Ribbon 界面风格的主窗口，支持自定义窗口样式（如是否使用 Ribbon 边框、菜单栏等），
 * 并根据样式自动配置窗口行为（如无边框、RibbonBar 安装等）。
 *
 * @param parent 父窗口部件
 * @param style 窗口样式标志，控制窗口外观和行为。支持以下标志组合：
 *        - @c SARibbonMainWindowStyleFlag::UseRibbonFrame：使用Ribbon自定义边框（会启用无边框窗口）（默认启用）。
 *        - @c SARibbonMainWindowStyleFlag::UseNativeFrame：使用系统原生窗口边框。
 *        - @c SARibbonMainWindowStyleFlag::UseRibbonMenuBar：使用 Ribbon 风格菜单栏（默认启用）。
 *        - @c SARibbonMainWindowStyleFlag::UseNativeMenuBar：使用系统原生菜单栏（非ribbon）。
 *        标志可通过位或（|）组合使用，例如：@c SARibbonMainWindowStyleFlag::UseRibbonFrame | SARibbonMainWindowStyleFlag::UseRibbonMenuBar
 *
 * @param flags 标准 Qt 窗口标志
 *
 * @note 如果启用了 @c UseRibbonFrame，则窗口将自动安装无边框支持。
 *
 * @sa SARibbonMainWindowStyleFlag, setRibbonBar(), ribbonTheme()
 */
SARibbonMainWindow::SARibbonMainWindow(QWidget* parent, SARibbonMainWindowStyles style, const Qt::WindowFlags flags)
    : QMainWindow(parent, flags), d_ptr(new SARibbonMainWindow::PrivateData(this))
{
    SA_D(d);
    d->mRibbonMainWindowStyle = style;
    d->checkMainWindowFlag();
    if (d->isUseRibbonBar()) {
        if (d->isUseRibbonFrame()) {
            d->installFrameless(this);
        }
        setRibbonBar(createRibbonBar());
        setRibbonTheme(ribbonTheme());
        setContentsMargins(2, 0, 2, 0);
        if (d->isUseNativeFrame()) {
            // 在ribbon模式下使用本地边框，将隐藏icon，同时默认设置为紧凑模式
            if (SARibbonBar* bar = ribbonBar()) {
                // 隐藏icon
                bar->setTitleIconVisible(false);
                // 设置为紧凑模式
                bar->setRibbonStyle(SARibbonBar::RibbonStyleCompactThreeRow);
            }
        }
    }
    connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonMainWindow::onPrimaryScreenChanged);
}

SARibbonMainWindow::~SARibbonMainWindow()
{
}

/**
 * @brief 返回ribbonbar，如果不是使用ribbon模式，返回nullptr
 * @return
 */
SARibbonBar* SARibbonMainWindow::ribbonBar() const
{
    return qobject_cast< SARibbonBar* >(menuWidget());
}

/**
 * @brief 设置ribbonbar
 * @param bar
 */
void SARibbonMainWindow::setRibbonBar(SARibbonBar* ribbon)
{
    QWidget* old = QMainWindow::menuWidget();
    if (old) {
        // 如果之前已经设置了menubar，要把之前的删除
        old->deleteLater();
    }
    QMainWindow::setMenuWidget(ribbon);
    ribbon->setMainWindowStyles(d_ptr->mRibbonMainWindowStyle);
    const int th = ribbon->titleBarHeight();

    if (d_ptr->isUseRibbonFrame()) {
        // 设置window按钮
        if (nullptr == d_ptr->mWindowButtonGroup) {
            d_ptr->mWindowButtonGroup = RibbonSubElementFactory->createWindowButtonGroup(this);
            d_ptr->mWindowButtonGroup->setObjectName(QStringLiteral("objSARibbonSystemButtonBar"));
            d_ptr->mWindowButtonGroup->setIconSize(QSize(18, 18));
            // SARibbonSystemButtonBar的eventfilter捕获mainwindow的事件
            // 通过eventerfilter来处理mainwindow的事件，避免用户错误的继承resizeEvent导致systembar的位置异常
            installEventFilter(d_ptr->mWindowButtonGroup);
        }
        SARibbonSystemButtonBar* sysBar = d_ptr->mWindowButtonGroup;
        sysBar->setWindowStates(windowState());
        sysBar->setWindowTitleHeight(th);
        sysBar->raise();  // 确保sysbar在最顶层，避免第二次设置ribbonbar的时候，被ribbonbar覆盖了sysbar
        sysBar->show();

        // 图标
        ribbon->titleIconWidget()->setIcon(windowIcon());
#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER
        auto helper = d_ptr->mFramelessHelper;
        helper->setTitleBar(ribbon);
        // 以下这些窗口，需要允许点击
        helper->setHitTestVisible(sysBar);                         // IMPORTANT!
        helper->setHitTestVisible(ribbon->ribbonTabBar());         // IMPORTANT!
        helper->setHitTestVisible(ribbon->rightButtonGroup());     // IMPORTANT!
        helper->setHitTestVisible(ribbon->applicationButton());    // IMPORTANT!
        helper->setHitTestVisible(ribbon->quickAccessBar());       // IMPORTANT!
        helper->setHitTestVisible(ribbon->ribbonStackedWidget());  // IMPORTANT!
        helper->setHitTestVisible(ribbon->titleIconWidget());      // IMPORTANT!
#if SARIBBON_ENABLE_SNAP_LAYOUT
        if (sysBar->closeButton()) {
            helper->setSystemButton(QWK::WindowAgentBase::Close, sysBar->closeButton());
        }
        if (sysBar->minimizeButton()) {
            helper->setSystemButton(QWK::WindowAgentBase::Minimize, sysBar->minimizeButton());
        }
        if (sysBar->maximizeButton()) {
            helper->setSystemButton(QWK::WindowAgentBase::Maximize, sysBar->maximizeButton());
        }
#endif
#else
        // 捕获ribbonbar的事件
        ribbon->installEventFilter(this);
        // 设置窗体的标题栏高度
        d_ptr->mFramelessHelper->setTitleHeight(th);
        d_ptr->mFramelessHelper->setRubberBandOnResize(false);
#endif
        // 最后要提升，否则新加入的会被覆盖
        if (d_ptr->mWindowButtonGroup) {
            d_ptr->mWindowButtonGroup->raise();
        }
    }
    if (!d_ptr->mEventFilter) {
        d_ptr->mEventFilter = new SARibbonMainWindowEventFilter(this);
        installEventFilter(d_ptr->mEventFilter);
    }
}

#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER

/**
 * @brief 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后，想能点击，需要调用此接口告知可点击
 * @param w
 * @param visible
 */
void SARibbonMainWindow::setFramelessHitTestVisible(QWidget* w, bool visible)
{
    auto helper = d_ptr->mFramelessHelper;
    helper->setHitTestVisible(const_cast< QWidget* >(w), visible);
}
#else

/**
 * @brief 无边框辅助对象
 * @return
 */
SAFramelessHelper* SARibbonMainWindow::framelessHelper() const
{
    return (d_ptr->mFramelessHelper);
}

/**
 * @brief 设置在缩放时是否启用“橡皮筋”示意模式。
 *
 * 当启用时，窗口在拖拽缩放过程中不会立即重绘内容，而是先用一个半透明矩形框（橡皮筋）
 * 显示目标尺寸，待用户释放鼠标后才一次性完成resize。此模式可显著降低
 * CAD、三维渲染、大型图表等重负载应用在高频resize时的CPU/GPU消耗。
 *
 * @param on  true  启用橡皮筋缩放示意；
 *             false 禁用橡皮筋，采用实时重绘（默认行为）。
 *
 * @see isRubberBandOnResize(), SAFramelessHelper::setRubberBandOnResize()
 */
void SARibbonMainWindow::setRubberBandOnResize(bool on)
{
    if (SAFramelessHelper* fl = framelessHelper()) {
        fl->setRubberBandOnResize(on);
    }
}

/**
 * @brief 返回当前是否启用了橡皮筋缩放示意模式。
 *
 * @return true  橡皮筋模式已启用；
 *         false 橡皮筋模式未启用
 *
 * @see setRubberBandOnResize(), SAFramelessHelper::rubberBandOnResize()
 */
bool SARibbonMainWindow::isRubberBandOnResize() const
{
    if (SAFramelessHelper* fl = framelessHelper()) {
        return fl->rubberBandOnResisze();
    }
    return false;
}
#endif

bool SARibbonMainWindow::eventFilter(QObject* obj, QEvent* e)
{
#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER
#else
    // 这个过滤是为了把ribbonBar上的动作传递到mainwindow，再传递到frameless，
    // 由于ribbonbar会遮挡掉frameless的区域，导致frameless无法捕获这些消息
    // 因此必须ribbonBar()->installEventFilter(this);

    if (obj == ribbonBar()) {
        switch (e->type()) {
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseMove:
        case QEvent::Leave:
        case QEvent::HoverMove:
        case QEvent::MouseButtonDblClick: {
            QApplication::sendEvent(this, e);
        } break;
        default:
            break;
        }
    }
#endif
    return (QMainWindow::eventFilter(obj, e));
}

/**
 * @brief 获取系统按钮组，可以在此基础上添加其他按钮
 * @return
 */
SARibbonSystemButtonBar* SARibbonMainWindow::windowButtonBar() const
{
    return d_ptr->mWindowButtonGroup;
}

/**
 * @brief 获取当前ribbonMainWidow的样式
 * @return
 */
SARibbonMainWindowStyles SARibbonMainWindow::ribbonMainwindowStyle() const
{
    return d_ptr->mRibbonMainWindowStyle;
}

/**
 * @brief 此函数仅用于控制最小最大化和关闭按钮的显示
 */
void SARibbonMainWindow::updateWindowFlag(Qt::WindowFlags flags)
{
    if (d_ptr->isUseRibbonFrame()) {
        if (SARibbonSystemButtonBar* g = d_ptr->mWindowButtonGroup) {
            g->updateWindowFlag(flags);
        }
    }
}

/**
 * @brief SARibbonMainWindow::setRibbonTheme
 *
 * 注意某些版本的qt，在构造函数设置主题会不完全生效，可以使用QTimer投放到队列最后执行，如：
 * @code
 * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); });
 * @endcode
 *
 * @param theme
 */
void SARibbonMainWindow::setRibbonTheme(SARibbonTheme theme)
{
    SA::setBuiltInRibbonTheme(this, theme);
    d_ptr->mCurrentRibbonTheme = theme;
    if (SARibbonBar* bar = ribbonBar()) {
        // 1. tab bar的间距
        if (SARibbonTabBar* tab = bar->ribbonTabBar()) {
            SARibbonMainWindow::PrivateData::updateTabBarMargins(tab, theme);
        }
        // 2. 上下文颜色设置
        SARibbonMainWindow::PrivateData::updateContextColors(bar, theme);
        // 3. tabbar的基线颜色
        SARibbonMainWindow::PrivateData::updateTabBarBaseLineColor(bar, theme);
    }
}

SARibbonTheme SARibbonMainWindow::ribbonTheme() const
{
    return (d_ptr->mCurrentRibbonTheme);
}

bool SARibbonMainWindow::isUseRibbon() const
{
    return (nullptr != ribbonBar());
}

/**
 * @brief 创建ribbonbar的工厂函数
 *
 * 用户如果重写了SARibbonBar，可以通过重新此虚函数返回自己的Ribbon实例
 * @return
 */
SARibbonBar* SARibbonMainWindow::createRibbonBar()
{
    SARibbonBar* bar = RibbonSubElementFactory->createRibbonBar(this);
    return bar;
}

/**
 * @brief 主屏幕切换触发的信号
 * @param screen
 */
void SARibbonMainWindow::onPrimaryScreenChanged(QScreen* screen)
{
    Q_UNUSED(screen);
    // 主屏幕切换后，从新计算所有尺寸
    if (SARibbonBar* bar = ribbonBar()) {
        qDebug() << "Primary Screen Changed";
        bar->updateRibbonGeometry();
    }
}

//----------------------------------------------------
// SARibbonMainWindowEventFilter
//----------------------------------------------------
SARibbonMainWindowEventFilter::SARibbonMainWindowEventFilter(QObject* par) : QObject(par)
{
}

SARibbonMainWindowEventFilter::~SARibbonMainWindowEventFilter()
{
}

bool SARibbonMainWindowEventFilter::eventFilter(QObject* obj, QEvent* e)
{
    if (e && obj) {
        if (e->type() == QEvent::Resize) {
            if (SARibbonMainWindow* m = qobject_cast< SARibbonMainWindow* >(obj)) {
                if (SARibbonBar* ribbon = m->ribbonBar()) {
                    QMargins mg = m->contentsMargins();
                    ribbon->setFixedWidth(m->size().width() - mg.left() - mg.right());
                }
            }
        }
    }
    return QObject::eventFilter(obj, e);
}

/*** End of inlined file: SARibbonMainWindow.cpp ***/

/*** Start of inlined file: SARibbonWidget.cpp ***/
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QScreen>

/**
 * @brief The SARibbonWidget::PrivateData class
 */
class SARibbonWidget::PrivateData
{
    SA_RIBBON_DECLARE_PUBLIC(SARibbonWidget)
public:
    PrivateData(SARibbonWidget* p);
    void installFrameless(SARibbonWidget* p);

public:
    SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue };
};

SARibbonWidget::PrivateData::PrivateData(SARibbonWidget* p) : q_ptr(p)
{
}

//===================================================
// SARibbonWidget
//===================================================
SARibbonWidget::SARibbonWidget(QWidget* parent) : QWidget(parent), d_ptr(new SARibbonWidget::PrivateData(this))
{
    // 直接创建SARibbonBar
    QVBoxLayout* verticalLayout = new QVBoxLayout(this);
    verticalLayout->setSpacing(1);
    verticalLayout->setObjectName("verticalLayout");
    verticalLayout->setContentsMargins(0, 0, 0, 0);
    SARibbonBar* ribbon = new SARibbonBar(this);
    setRibbonBar(ribbon);
    connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonWidget::onPrimaryScreenChanged);
}

SARibbonWidget::~SARibbonWidget()
{
}

/**
 * @brief 返回ribbonbar，如果不是使用ribbon模式，返回nullptr
 * @return
 */
SARibbonBar* SARibbonWidget::ribbonBar() const
{
    QLayout* lay = layout();
    if (lay) {
        return qobject_cast< SARibbonBar* >(lay->menuBar());
    }
    return nullptr;
}

/**
 * @brief 设置ribbonbar
 * @param bar
 */
void SARibbonWidget::setRibbonBar(SARibbonBar* bar)
{
    QLayout* lay = layout();
    if (lay) {
        lay->setMenuBar(bar);
    }
}

/**
 * @brief SARibbonMainWindow::setRibbonTheme
 *
 * 注意主题在构造函数设置主题会不完全生效，使用QTimer投放到队列最后执行即可
 * @code
 * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); });
 * @endcode
 * @param theme
 */
void SARibbonWidget::setRibbonTheme(SARibbonTheme theme)
{
    SA::setBuiltInRibbonTheme(this, theme);
    d_ptr->mCurrentRibbonTheme = theme;
    if (SARibbonBar* bar = ribbonBar()) {
        auto theme = ribbonTheme();
        // 尺寸修正
        switch (theme) {
        case SARibbonTheme::RibbonThemeWindows7:
        case SARibbonTheme::RibbonThemeOffice2013:
        case SARibbonTheme::RibbonThemeOffice2016Blue:
        case SARibbonTheme::RibbonThemeDark:
        case SARibbonTheme::RibbonThemeDark2: {
            //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中
            //! office2013.qss的margin信息如下设置
            //! margin-top: 0px;
            //! margin-right: 0px;
            //! margin-left: 5px;
            //! margin-bottom: 0px;
            SARibbonTabBar* tab = bar->ribbonTabBar();
            if (!tab) {
                break;
            }
            tab->setTabMargin(QMargins(5, 0, 0, 0));
        } break;
        case SARibbonTheme::RibbonThemeOffice2021Blue: {
            SARibbonTabBar* tab = bar->ribbonTabBar();
            if (!tab) {
                break;
            }
            //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中
            //! office2021.qss的margin信息如下设置
            //! margin-top: 0px;
            //! margin-right: 5px;
            //! margin-left: 5px;
            //! margin-bottom: 0px;
            tab->setTabMargin(QMargins(5, 0, 5, 0));
        }
        default:
            break;
        }
        // 上下文标签颜色设置,以及基线颜色设置
        switch (theme) {
        case SARibbonTheme::RibbonThemeWindows7:
        case SARibbonTheme::RibbonThemeOffice2013:
        case SARibbonTheme::RibbonThemeDark:
            bar->setContextCategoryColorList(QList< QColor >());  //< 设置空颜色列表会重置为默认色系
            break;
        case SARibbonTheme::RibbonThemeOffice2016Blue:
            bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120));  //< 设置空颜色列表会重置为默认色系
            break;
        case SARibbonTheme::RibbonThemeOffice2021Blue:
            bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209));  //< 设置空颜色列表会重置为默认色系
            break;
        default:
            break;
        }
        // 基线颜色设置
        if (SARibbonTheme::RibbonThemeOffice2013 == theme) {
            bar->setTabBarBaseLineColor(QColor(186, 201, 219));
        } else {
            bar->setTabBarBaseLineColor(QColor());
        }
    }
}

SARibbonTheme SARibbonWidget::ribbonTheme() const
{
    return (d_ptr->mCurrentRibbonTheme);
}

bool SARibbonWidget::isUseRibbon() const
{
    return (nullptr != ribbonBar());
}

/**
   @brief 设置窗口

   @param 窗口指针
   @note 窗口的所有权归SARibbonWidget管理
   @sa widget
   @note 原来设置的窗口会被delete
 */
void SARibbonWidget::setWidget(QWidget* w)
{
    QWidget* oldwidget = takeWidget();
    if (oldwidget) {
        oldwidget->hide();
        oldwidget->deleteLater();
    }
    if (QLayout* lay = layout()) {
        lay->addWidget(w);
    }
}

/**
   @brief 获取设置的窗口

    @return 如果没有，返回nullptr
 */
QWidget* SARibbonWidget::widget() const
{
    QLayout* lay = layout();
    if (lay) {
        if (lay->count() == 0) {
            return nullptr;
        }
        return lay->itemAt(0)->widget();
    }
    return nullptr;
}

QWidget* SARibbonWidget::takeWidget()
{
    QLayout* lay = layout();
    if (nullptr == lay) {
        return nullptr;
    }
    if (lay->count() == 0) {
        return nullptr;
    }
    auto item = lay->itemAt(0);
    if (nullptr == item) {
        return nullptr;
    }
    lay->removeItem(item);
    auto w = item->widget();
    delete item;
    if (!w) {
        return nullptr;
    }
    return w;
}

/**
 * @brief 主屏幕切换触发的信号
 * @param screen
 */
void SARibbonWidget::onPrimaryScreenChanged(QScreen* screen)
{
    Q_UNUSED(screen);
    // 主屏幕切换后，从新计算所有尺寸
    if (SARibbonBar* bar = ribbonBar()) {
        qDebug() << "Primary Screen Changed";
        bar->updateRibbonGeometry();
    }
}

/*** End of inlined file: SARibbonWidget.cpp ***/

/*** Start of inlined file: SARibbonApplicationWidget.cpp ***/
#include <QEvent>
#include <QKeyEvent>
#include <QResizeEvent>
#include <QDebug>
#include <QPainter>

SARibbonApplicationWidget::SARibbonApplicationWidget(SARibbonMainWindow* parent) : QFrame(parent)
{
    setWindowFlags(windowFlags() | Qt::FramelessWindowHint);  // 去除边框
    parent->installEventFilter(this);
    if (parent) {
        setGeometry(0, 0, parent->width(), parent->height());
    } else {
        setGeometry(0, 0, 300, 300);
    }
}

void SARibbonApplicationWidget::resizeToParent(const QSize& parentSize)
{
    setGeometry(0, 0, parentSize.width(), parentSize.height());
}

bool SARibbonApplicationWidget::eventFilter(QObject* obj, QEvent* ev)
{
    if (obj && ev && (obj == parent())) {
        switch (ev->type()) {
        case QEvent::Resize: {
            // 跟随父窗口的尺寸变化
            resizeToParent(static_cast< QResizeEvent* >(ev)->size());
            break;
        }
        default:
            break;
        }
    }
    return QFrame::eventFilter(obj, ev);
}

void SARibbonApplicationWidget::showEvent(QShowEvent* event)
{
    QWidget* par = parentWidget();
    if (par) {
        resizeToParent(par->size());
    }
    setFocus();  // 显示窗口时请求焦点
    QFrame::showEvent(event);
}

/**
 * @brief 按下exe键隐藏窗口
 * @param ev
 */
void SARibbonApplicationWidget::keyPressEvent(QKeyEvent* ev)
{
    if (ev) {
        if (ev->key() == Qt::Key_Escape) {
            hide();
            qDebug() << "Key_Escape";
            ev->accept();
        }
    }
    return QFrame::keyPressEvent(ev);
}

/*** End of inlined file: SARibbonApplicationWidget.cpp ***/

#ifdef _MSC_VER
#pragma warning(pop)
#pragma pop_macro("_CRT_SECURE_NO_WARNINGS")
#endif
